1
0
mirror of https://github.com/OpenTTD/OpenTTD.git synced 2025-08-14 10:09:11 +00:00

Compare commits

..

162 Commits
0.5.2 ... 0.4.8

Author SHA1 Message Date
Darkvater
92f5aa81ab (svn r5863) Release 0.4.8 2006-08-12 12:38:43 +00:00
Darkvater
4e11b36000 (svn r5862) - Prepare 0.4 branch for release. Update readme's, bugs, installers, changelog, etc. to 0.4.8 2006-08-12 12:35:23 +00:00
Darkvater
2452a20e07 (svn r5861) - Backport from trunk (r5839):
A ship in a depot must be stopped before allowed to be cloned (copied ship.h from trunk).
2006-08-12 12:22:38 +00:00
Darkvater
ffa39381d9 (svn r5858) - Backport from trunk (r5801):
The exception dialog showed the last modification-date of win32.c instead of the
  last compilation-date.
2006-08-12 12:13:08 +00:00
Darkvater
f2e4b69d5c (svn r5857) - Backport from trunk (r5800):
When directories were changed in 'Play Scenario', pressing 'New Game' showed
  scenarios from that directory, not the default one.
2006-08-12 12:11:54 +00:00
Darkvater
a597fd50e6 (svn r5856) - Backport from trunk (r3598):
Suppress invalid warning by assigning value to variable
2006-08-12 12:01:20 +00:00
Darkvater
6f920cee6c (svn r5678) - Prepare 0.4 branch for release. Update readme's, bugs, installers, changelog, etc. to 0.4.8-RC2 2006-07-31 15:49:12 +00:00
Darkvater
bd736e240a (svn r5674) - Backport from trunk (r5664):
Certain combinations of trains crash when moved around inside the depot.
2006-07-31 12:12:14 +00:00
Darkvater
6c3eedee86 (svn r5673) - Backport from trunk (r5655):
Reversed arrow-sign in the multiplayer list column headers on sort by name
2006-07-31 12:11:14 +00:00
Darkvater
9214c5e0a5 (svn r5672) - Backport from trunk (r5652):
Industry production change button doesn't work for oilrig passangers.
2006-07-31 12:10:20 +00:00
Darkvater
6af2e64186 (svn r5671) - Backport from trunk (r5504, r5512):
Added Italian town name generator. While not a fix, it is added along the same lines
  as the turkish town names. Official translation > official townnames (if existing).
2006-07-31 12:08:08 +00:00
Darkvater
58b4fd7683 (svn r5669) - Backport from trunk (r5464, r3641):
Codechange: verify the presence of music files in the gm folder. Slightly altered r5464
  to exclude the addition of music.c and left out the extra functionality. While in essence
  this is not a true fix, several people have reported a rising CPU usage because Dmusic
  kept indefinitely looping the file list. This should solve that.
2006-07-31 11:50:23 +00:00
Darkvater
f3cce610c8 (svn r5668) - Backport from trunk (r5454):
Helicopters stopping in depot after autorenew/autoreplace
2006-07-31 11:29:18 +00:00
Darkvater
5c30032fe5 (svn r5666) - Backport from trunk (Webtranslator2):
Language changes. Galician has 28 untranslated strings.
2006-07-30 23:43:47 +00:00
tron
6708e181eb (svn r5499) Fix a case of an uninitialised variable in r5368 which caused some graphical glitches at foundations (wrong foundation borders, flickering)
This problem only exists in the backport
2006-07-15 09:21:29 +00:00
truelight
d81a7bf904 (svn r5498) -Fix: in r4677, Darkvater ported patch r4508 from trunk wrongly into the 0.4 branch. Because of that stations no longer delivered goods to industries if they were more then 2 tiles away, instead of the allowed station_spread * 2. 2006-07-14 18:45:31 +00:00
truelight
9c95e99871 (svn r5492) -Backport (r5491) -Fix: [#9] MorphOS crashed when you go a level up on root level (tokai) 2006-07-13 18:20:51 +00:00
truelight
8ecd975951 (svn r5490) -Backport (r5489) -Fix: corrected tokai's name and morphos details (tokai) 2006-07-13 17:58:55 +00:00
truelight
955d4393e0 (svn r5488) -Backport (r5487) -Fix: [#8] UDP sockets were used even if network-availability was set to false (tokai) 2006-07-13 17:54:57 +00:00
bjarni
f0ba57ea82 (svn r5440) -Backport: rev 5428 -Fix: [vehicles] sovled crash when trying to build a vehicle type, that is set to max 0 (spotted by roboman) 2006-07-01 10:46:50 +00:00
bjarni
ebae6200c0 (svn r5439) -Backport [OSX] rev 5438 -Code cleanup [OSX] removed UNIVERSAL_BINARY as a phony target since the target is long gone. Also corrected BUILD_OSX_BUNDLE as phony target
also corrected two $(BUILD_OSX_BUNDLE) to BUILD_OSX_BUNDLE, so the bundle will always build
2006-07-01 10:44:32 +00:00
47fc5a070b (svn r5424) - Correct typo in the date of the man file 2006-06-29 05:54:16 +00:00
Darkvater
a22933719b (svn r5414) Also update the readme file to 0.4.8 2006-06-28 23:04:03 +00:00
Darkvater
5fe523dfcd (svn r5410) - Prepare 0.4 branch for release. Update readme's, bugs, installers and makefile, changelog, etc. to 0.4.8 2006-06-28 21:28:58 +00:00
Darkvater
514eee8067 (svn r5408) - Backport from trunk (Webtranslator2):
Language changes. Galician, Icelandic seems to have lazy translators..
2006-06-28 20:09:32 +00:00
Darkvater
3ed386c180 (svn r5407) - Backport from trunk (r5397):
Redraw the screen when switching the signal side
2006-06-28 20:05:02 +00:00
tron
22df8a8d6b (svn r5369) -Backport: 5363, 5364, 5365
-Fix: It was possible to dig into a tunnel if certain rail combinations were ontop of it
2006-06-26 15:59:58 +00:00
tron
a2fc417d86 (svn r5368) -Backport: 5351, 5352
-Fix: Several graphical glitches at adjacent tiles with foundations. Some borders were missing, some were superfluous
-Fix: Return accurate slope information for tunnels and bridges to fix several foundation graphics glitches
2006-06-26 15:00:23 +00:00
tron
339160760b (svn r5367) -Backport: 5348
-Fix: A HQ could only be flooded at its northern tile, the other 3 were immune to water
2006-06-26 14:58:41 +00:00
tron
93713354a2 (svn r5356) Move the inclusion of slope.h to reduce diff to trunk 2006-06-25 09:07:49 +00:00
tron
e3e64430d7 (svn r5350) -Backport: r5327
Use DrawFoundation() for houses
-Fix: Some graphical glitches on house tiles with foundations
-Fix: The selection cursor is now aligned with the top of the foundation for house tiles
2006-06-24 09:48:51 +00:00
tron
20b5c17faa (svn r5349) -Backport: r5315
-Fix: Prohibit altering a road tile while road works are in progress
This fixes some glitches like "turning" the excavation by adding/removing road bits or removing the road piece
2006-06-24 09:12:15 +00:00
tron
f0fd9921a2 (svn r5331) -Backport: r5294
-Fix: Plug a memory leak
2006-06-21 19:39:54 +00:00
tron
47a1e50406 (svn r5330) -Backport: r5292, r5293, r5295, r5297
-Fix: When using SIOCGIFCONF to detect network interfaces accomodate for the fact that struct sockaddr doesn't have fixed size in all implementations
-Fix: Not all network interfaces are capable of broadcasting. Don't record those which aren't
-Fix: Not all networks are /24. Generate proper broadcast addresses for non-/24 nets
2006-06-21 19:39:23 +00:00
tron
c96daf6230 (svn r5329) -Backport: r5291
-Fix: '-f' switch is not valid on windows, so don't show it in help
2006-06-21 19:35:50 +00:00
Darkvater
257ba823a0 (svn r5290) - Backport from trunk (r's and lots of it):
Language changes. Galician (29 missing) and Icelandic (20 missing) are bad off.
  Norwegian, Brazilian-Portugese (2 missing) and Slovak (1 missing).
2006-06-16 19:43:39 +00:00
Darkvater
7fd164deba (svn r5289) - Backport from trunk (r5175, r5176):
Autoreplaced trains can leave all wagons in depot under certain circumstances
2006-06-16 18:59:26 +00:00
tron
73dab80259 (svn r5284) -Backport: r5264
-Fix: The wrong IP could get unbanned, e.g. 'unban 1.2.3.42' could result in unbanning 1.2.3.4
2006-06-15 16:47:13 +00:00
tron
91fc18dcdc (svn r5283) -Backport: r5260
-Fix: It was possible to convert the railtype of a bridge while a train was on it
2006-06-15 16:45:29 +00:00
tron
6d4c3f9636 (svn r5282) -Backport: r5226
-Fix: It was possible to rename signs or waypoints with the chat box
2006-06-15 15:25:18 +00:00
tron
d070a97f7a (svn r5281) -Backport: r5124
-Fix: Be more strict what it means for an aircraft to be in a hangar: It's not just being stopped on a hangar tile
2006-06-15 14:45:03 +00:00
tron
a8d2aa157a (svn r5280) -Backport: r5119
-Fix: If a road vehicle is on a road depot tile and stopped doesn't mean it's in the depot. Use the proper test for this
2006-06-15 14:34:31 +00:00
tron
af0354ad24 (svn r5279) -Backport: r4116
-Fix: The AI should send a plane into a hangar if it's not in a hangar _or_ not stopped, not when it's not in a hangar _and_ not stopped
2006-06-15 14:16:57 +00:00
tron
12a68cc7c3 (svn r5115) Move helper functions to where they belong 2006-06-05 08:30:58 +00:00
Darkvater
45ea5388fe (svn r5110) - Backport from trunk (r's and lots of it):
Language changes. Galician (67 missing!) and Icelandic (20 missing) are bad off.
  Spanish, Norwegian, Brazilian-Portugese (2 missing) and Slovak (1 missing).
2006-06-04 22:34:52 +00:00
Darkvater
1be4771689 (svn r5109) - Backport from trunk (r5097):
The trolly AI used information from the wrong industry when calculating the
  amount of to be transported goods
2006-06-04 22:03:58 +00:00
Darkvater
590cd1d07b (svn r5108) - Backport from trunk (r5092):
There was a gross race condition in the AI code which made it pretty random
  if the AI could give a new vehicle its orders
2006-06-04 22:02:51 +00:00
Darkvater
4f27e53d17 (svn r5107) - Backport from trunk (r5085, r5088):
Add parentheses to CHANCE16*() macro parameters
2006-06-04 21:58:48 +00:00
Darkvater
f026c645e3 (svn r5106) - Backport from trunk (r4964):
Fix NTP over bridges: don't check the rail type when on a bridge
2006-06-04 21:40:21 +00:00
Darkvater
00f2b3e713 (svn r5102) - Backport from trunk (r4753):
Truncate text in dropdown lists to stop text overflowing.
2006-06-04 17:25:00 +00:00
ca175e873f (svn r5072) - Backport from trunk (r5071):
- Fix (FS#184): "Erroneous train reversal on waypoints". When processing the next train order, do not even consider reversing the train if the last order was to a waypoint.
2006-06-02 13:23:22 +00:00
Darkvater
86e4f20be8 (svn r4947) - Fix [FS#145]: Starting scenarios did not adhere to local difficulty settings 2006-05-22 16:44:16 +00:00
Darkvater
d24165ea71 (svn r4932) - Codechange: (r4931): move GetTileMaxZ to tile.[ch] instead of lingering it in tunnelbridge_cmd.c. Might be needed some day for some other backport commit (Tron). 2006-05-20 20:56:31 +00:00
Darkvater
c2e9eb7d7b (svn r4931) - Backport from trunk (r4766):
Vehicles on a sloped tile under a bridge were affected by the bridge speed limit
2006-05-20 20:16:08 +00:00
Darkvater
ec9870a611 (svn r4930) - Backport from trunk (r4859):
Fix issue with train pathfinding over level crossings.
2006-05-20 18:43:59 +00:00
Darkvater
1f9a69bc89 (svn r4929) - Backport from trunk (r's and lots of it):
Language changes. It seems no strings are missing, good job translators :D
2006-05-20 18:41:11 +00:00
Darkvater
a707c043fc (svn r4926) - Backport from trunk (r4914):
Aircraft can now serve as feeders
2006-05-20 17:36:06 +00:00
Darkvater
587cedc35b (svn r4925) - Backport from trunk (r4911):
The AI no longer attempts to build signals under bridges.
2006-05-20 17:35:11 +00:00
Darkvater
f5e0e18dca (svn r4924) - Backport from trunk (r4906):
Refresh build vehicle window (if opened) when converting rail depot
2006-05-20 17:33:36 +00:00
Darkvater
5c8bd4b143 (svn r4923) - Backport from trunk (r4892):
Crash when sorting an empty server list.
2006-05-20 17:22:32 +00:00
Darkvater
d1ca343504 (svn r4922) - Backport from trunk (r4827):
The build-tree window button defaulted to a place-push-button on opening where
  no treetype is selected.
2006-05-20 17:14:54 +00:00
Darkvater
a27b563c40 (svn r4921) - Backport from trunk (r4825, r4826, r4829):
Game crashes when cloning/autoreplace reaches train-limit
2006-05-20 17:11:09 +00:00
Darkvater
f6e8773297 (svn r4919) - Backport from trunk (r4812, r4008, r4110):
NTP properly checks for railtypes on non-plain-rail-tiles
2006-05-20 15:54:46 +00:00
Darkvater
0f012198c4 (svn r4894) - Backport from trunk (r4750):
Trains could enter certain sloped rail tiles under bridges with incompatible rail type
2006-05-16 22:11:15 +00:00
Darkvater
d27bedce36 (svn r4893) - Fix (FS#57): Disable NPF totally for ships as it wholly kills performance (blathijs). Only for 0.4/ branch and 0.4.8. 2006-05-16 21:38:41 +00:00
Darkvater
8892c1d1de (svn r4731) - Backport from trunk (r4203):
Fix: compile on older mingw32 versions (3.1.0)
2006-05-03 21:57:09 +00:00
Darkvater
660d991cc1 (svn r4730) - Backport from trunk (r4690):
Fix (r4668, br4301): Editing a too long string in the editbox resulted in improper strings
  Update about box with Mihamix's real name
2006-05-03 21:40:45 +00:00
Darkvater
63a1bc7ee7 (svn r4728) - Backport from trunk (r4689):
Codechange: correct parameter order or calloc, and use the sizeof
  the variable rather than a struct
2006-05-03 21:35:10 +00:00
Darkvater
2aba4c3354 (svn r4727) - Backport from trunk (r4688):
Fix: Ensure the map memory is cleared after it is allocated. This fixes
  random deserts that sometimes occurred.
2006-05-03 21:34:07 +00:00
Darkvater
4021f48c58 (svn r4726) - Backport from trunk (r4373, r4374, r4402):
Fix: Some weird behaviour with tile selection near bridges
2006-05-03 21:28:48 +00:00
Darkvater
ca6ccf035f (svn r4718) - Backport from trunk (r4715):
Fix: Don't allow PF to enter train depot from the back (signal updates)
2006-05-03 20:25:10 +00:00
Darkvater
f2143b3d9b (svn r4717) - Backport from trunk (r4466):
Fix: Game no longer crashes when the last vehicle serving a station has been
  deleted. This is not exactly the same fix as in trunk/ where it might still
  accept types of invalid types but it doesn't crash anymore. The true fix is
  not possible without a savegame bump.
2006-05-03 20:09:28 +00:00
Darkvater
9c97eb32ba (svn r4701) - Backport from trunk (r4520):
Fix: Reset the last built railtype when starting a new game
2006-05-02 21:19:48 +00:00
Darkvater
f9fc273d2a (svn r4695) - Backport language changes from trunk up to r4592. 2006-05-02 19:52:56 +00:00
Darkvater
343fdd71ef (svn r4687) - Backport from trunk (r4674):
Codechange: use correct parameter order for allocating sound file memory
2006-05-02 14:14:04 +00:00
Darkvater
3d8b1c4fec (svn r4686) - Backport from trunk (r4639):
Feature: Turksih town names (Turkish is in the lang/ folder, so
  town names are appropiate)
2006-05-02 14:12:19 +00:00
Darkvater
00c45347fd (svn r4685) - Backport from trunk (r4599):
Fix: Cloned vehicles get the same service interval as the original vehicle
2006-05-02 14:06:15 +00:00
Darkvater
6fc56409e6 (svn r4684) - Backport from trunk (r4591):
Fix: Game no longer errors out when "Many random towns" is selected
  in the scenario editor.
2006-05-02 14:02:23 +00:00
Darkvater
8e4cf4d599 (svn r4683) - Backport from trunk (r4586):
Codechange: Recursive commands that rely on _error_message to handle
  success/failure can fail if a recursive call fails but doesn't set the 
  error message, thus resulting in an old, possibly erroneous being used
2006-05-02 14:00:26 +00:00
Darkvater
52d25fe06f (svn r4682) - Backport from trunk (r4585, r4950):
Fix: Obscure road dragging bug. The road build command did not 
  return the appropiate error message of invalid-slope when building road.
2006-05-02 13:58:43 +00:00
Darkvater
17b691188d (svn r4681) - Backport from trunk (r4576):
Fix : Temperate bank will no longer appear (during game) in
  tropic landscape. This bug is from the original game.
2006-05-02 13:50:30 +00:00
Darkvater
ec18908c82 (svn r4680) - Backport from trunk (r4560):
Fix: Remove VS2005 undefined vsnprintf() as it doesn't exist
  in the windows libraries (only _vsnprintf). This caused the bad
  function to be called that could result in non-properly terminated
  strings
2006-05-02 13:48:33 +00:00
Darkvater
271af9fbfc (svn r4679) - Backport from trunk (r4521):
Fix: be consistent about the size of the player-name in MP. This
  hopefully fixes a crash on lesser OS's (eg Win98)
2006-05-02 13:47:00 +00:00
Darkvater
ba6fcc84c8 (svn r4678) - Backport from trunk (r4518, r4558):
Fix: specify the 'stopall' console command as a debug command.
2006-05-02 13:44:41 +00:00
Darkvater
a8ddd50157 (svn r4677) - Backport from trunk (r4508):
Fix: Fixed a problem that caused DeliverGoodsToIndustry to not work 
  as intended
2006-05-02 13:42:33 +00:00
Darkvater
dcfd3acc8f (svn r4676) - Backport from trunk (r4505):
Fix: Ships can now be used to set up feeders as well.
2006-05-02 13:37:36 +00:00
Darkvater
d5bcaec677 (svn r4675) - Backport from trunk (r4501):
Fix: When, in a train that has multiple engines in front, the _first_
  of those engines is sold, all the orders are copied to the second
  engine (to ensure "seamless" operation). The next_shared/prev_shared
  pointers where not updated correctly during this operation.
2006-05-02 13:36:38 +00:00
Darkvater
a336c7cd0c (svn r4673) - Backport from trunk (r4468):
Correct declaration of NORETURN for gcc
2006-05-02 13:30:13 +00:00
Darkvater
a343d86500 (svn r4672) - Backport from trunk (r4467):
New plantations now cause the correct ".. being planted .." news item
2006-05-02 13:29:06 +00:00
Darkvater
61370f9e14 (svn r4671) - Backport from trunk (r4445):
Danish town names were saved/loaded as Swiss (previous in the list)
2006-05-02 13:27:19 +00:00
Darkvater
f7228e0b3a (svn r4670) - Backport from trunk (r4435):
In CmdRemoveRoad tiletype was not checked for ownership (heavily edited)
2006-05-02 13:25:03 +00:00
Darkvater
eb703a5768 (svn r4669) - Backport from trunk (r4343):
[Autoreplace] Fix drawing of train list for outdated engines.
2006-05-02 13:11:00 +00:00
Darkvater
f0e3fa29e5 (svn r4668) - Backport from trunk (r4301):
Fix: the maxlength parameter of Textbuf is supposed to be the siz
  of the buffer (so length of string + '\0'), but in the code it 
  was a mix of both.
2006-05-02 13:07:23 +00:00
Darkvater
aba21dd563 (svn r4667) - Backport from trunk (r4291):
Fix: validate all received strings for correctness. This fixes
  potential crashes on hacked clients/servers
2006-05-02 13:00:07 +00:00
Darkvater
f8eb72e188 (svn r4666) - Backport from trunk (r4267):
Validate the error number that a client receives from a server, and
  encapsulate this functionality into GetNetworkErrorMsg()
2006-05-02 12:58:13 +00:00
Darkvater
f9a2d113ab (svn r4665) - Backport from trunk (r4241, r4243):
Fix: Perform validation on the error number that a server receives
  from a client. An invalid value may cause the server to terminate.
2006-05-02 12:54:23 +00:00
Darkvater
f26027da17 (svn r4664) - Backport from trunk (r4228):
Fix: [autoreplace] allow replacement of wagons even when the
  engine fails to be replaced
2006-05-02 12:52:09 +00:00
Darkvater
a32fdb390f (svn r4663) - Backport from trunk (r4195):
Codechange: Initialize order variables to avoid a compiler warning.
2006-05-02 12:47:51 +00:00
Darkvater
8bfb955d4b (svn r4662) - Backport from trunk (r4192):
Fix: In the depot, moving wagons from the end of a very long train
  to a short train where the short train was scrolled off the window
  would cause a game crash.
2006-05-02 12:44:55 +00:00
Darkvater
3a3a1da4e7 (svn r4661) - Backport from trunk (r3865-r3867, r3869, r3870, r3873, r3894, r4059, r4064-r4066, r4217):
Add: a fully optional configure script, that is a wrapper
  around makefile.config
2006-05-02 12:42:21 +00:00
Darkvater
ec47441ce4 (svn r4660) - Backport from trunk (r4183, r4197, r4217):
Codechange: [Makefile]: removed MANUAL_CONFIG as it's not used anymore
  This should hopefully fix the issue where WITH_SDL can be defined while
  SDL_CONFIG is not. Added an error if WITH_SDL is defined but SDL_CONFIG
  is not.
  Replace the dash of SDL_CONFIG/LIBPNG_CONFIG with an underscore
2006-05-02 12:28:35 +00:00
Darkvater
39ebb55b9b (svn r4659) - Backport from trunk (r4158):
Fix: [autoreplace] cost for refitting the new vehicle is now
  added to the cost animation. The player always paid for it, but
  it was not displayed until now
2006-05-02 12:17:16 +00:00
bjarni
c59beffe6b (svn r4624) -Backported r4149 from trunk
main reason is that it fixes the load/save issue for OSX 10.3.9, but the other stuff in this commit can't be taken as it's a result of the fix

full commit log entry:
  -Codechange: [OSX] rewrite of how universal binaries are compiled

    Now OSX stores object files in .OSX and instead of making FAT object files, there are one for each architecture
    Each architecture got their own targets to make a non-FAT binary and in the end, lipo will merge them into one binary

    It's now possible to select which architectures you want to support by defining OTTD_PPC, OTTD_PPC970 (G5) and/or OTTD_i386
    All combos are supported. UNIVERSAL_BINARY and TRIPLE_BINARY can still be used even though it's possible to gain the same result by using the new flags
    Making a universal build when you already got part of it compiled (say the PPC part), it will reuse it and only compile the i386 part to save time
    Note: in some cases when you switch flags, you risk that openttd is not updated. Delete it and try again. The Makefile can't solve this except if it forces linking each time

    This fixes: FS#87 universal binary building borked in 0.4.7
    Now universal binaries work on OSX 10.3.9 again

    Building universal binaries no longer needs to store flags in Makefile.config as the new design makes it possible to figure everything out automatically
2006-04-29 14:38:21 +00:00
celestar
2576164325 (svn r4608) -Backported r4413 from trunk:
-Fix: fixed a bug which pushed the client back to the main menu when a
	server is in the mainserver-list which sends out illegal signals. Many
	tnx to 'test' for finding and isolating the problem.
	-Fix: also specify the problem a bit better
2006-04-28 07:53:10 +00:00
celestar
bacbe211e7 (svn r4607) -Backported r4389 from trunk:
-Fix: [NPF] Don't mark tiles when debugging in multiplayer, this will cause desyncs
2006-04-28 07:51:32 +00:00
celestar
0381a100fa (svn r4606) -Backported r4341 from trunk:
-(FS#101) When a player got bankrupt, slots were not cleared, because vehicles got deleted directly by DeleteVehicle
2006-04-28 07:47:55 +00:00
celestar
6573b46ca3 (svn r4605) -Backported revisions 4304, 4309, 4310, 4312, 4313, 4314 from trunk (chatbox-related stuff)
-The chat box' parent window (the main toolbar, wtf?) doesn't care for the WE_ON_EDIT_TEXT_CANCEL event, so don't send one. This code looks like it was mindlessly copy&pasted from the query box
	-The initial string of the chat box is always the empty string, so don't jump through hoops to check if nothing was entered and simplify the code
	-The chat box has no visible window title, therefore remove the string
	-Calculate the maximum pixel width of the entered text in the chat box from the "text box"-widget instead of hardcoding an arbitrary - and wrong - number
	-The parent window of the chat box is always the main toolbar (?!), therefore don't pass this information as parameter
	-a buffer overflow of the chat box introduced in r1263. Don't tell the Textbuf an arbitrary number as size of the string buffer, but the real lengthof() it
2006-04-28 07:45:44 +00:00
orudge
05b541f63a (svn r4207) - Fix: Update OS/2 on 0.4 branch, too 2006-03-31 16:19:16 +00:00
Darkvater
8a74170da0 (svn r4117) - Prepare 0.4 branch for release. Update readme's, bugs, installers and makefile, changelog, etc. to 0.4.7 2006-03-26 18:49:31 +00:00
bjarni
a0bf18c3c9 (svn r4104) -Backported 4102: updated OSX docs about triple binary 2006-03-25 08:54:09 +00:00
bjarni
31b226dc9f (svn r4102) -Backported 3673, 3674, 3675, 3679, 3682, 3882, 3884 [all OSX]
added support for G5 (ppc970) optimised code
	added support for triple binaries (ppc, ppc970, i386)
	updated the makefile to handle building of universal and triple in a more automated way (way less flags to set)
	now it's no longer needed to spent minutes setting up flags when releasing. It works out of the box :)
	note: even though it looks like it's a lot of commits, it really is that there changes were introduced in small pieces in the trunk
2006-03-25 08:45:30 +00:00
bjarni
b320aa40fa (svn r4100) -Backported 4082 and 4099
the cocoa driver no longer crashes when going to fullscreen (this one depended on the resolution)
	teh cocoa driver speedup of around 1000% applies to Intel macs, so the driver is now just as fast as the PPC one
2006-03-25 07:40:02 +00:00
bjarni
37835e9158 (svn r4097) -Backported 4084 properly (included the last line)
also added a header that is needed because we didn't backport the new feature, that includes the header in the trunk
2006-03-24 23:33:30 +00:00
bjarni
749a4f8c7d (svn r4095) -Backport 4060, 4084
made the release target for OSX do more work (less manual work when releasing)
	updated some OSX documentation
2006-03-24 22:55:16 +00:00
e72232dc4c (svn r4094) - Allow unused wagons have their ->first set. This fixes the faulty
cache warning message, and noticably speeds up depot operations in large 
games. Backport of r3576 from trunk
2006-03-24 22:36:54 +00:00
matthijs
45a4c69842 (svn r4072) - Backport from trunk (4071):
- Fix: [NPF] Trains & busses were unable to find a route when leaving a depot or bus stop. Small omission from r4023 (fix by glx)
2006-03-23 17:52:08 +00:00
matthijs
a2c882af6f (svn r4044) Rename 0.4.5 branch to 0.4. Further minor releases will be in the 0.4 range, to prevent enormously long version numbers. 2006-03-22 22:38:29 +00:00
matthijs
bb7c37b515 (svn r4041) [Debian] Change next version number to 0.4.6 instead of 0.4.5.1. 2006-03-22 22:26:16 +00:00
Darkvater
ec54b3ac24 (svn r4040) - Prepare 0.4.5 branch for release. Update readme's, bugs, installers and makefile, changelog, etc. to 0.4.6 2006-03-22 22:25:46 +00:00
bjarni
a9032183df (svn r4038) -backport (3966, 3972 and 4019) -Fix: [OSX 10.3 and newer] [ 1157244 ] Can't save game if name contains german umlauts (loading savegames with certain chars still look a bit odd) 2006-03-22 21:40:26 +00:00
bjarni
90feff4982 (svn r4037) -backported (3676): updated the install readme for OSX 2006-03-22 21:24:26 +00:00
matthijs
e434485dd8 (svn r4036) * Prepare debian release files for 0.4.5.1 release. 2006-03-22 21:18:33 +00:00
matthijs
dcc4ccf4e9 (svn r4035) - Backport from trunk (4033):
- Codechange: [Debian] Update debian packaging files to use debconf for user interaction.
2006-03-22 21:16:31 +00:00
bjarni
ef7e4abf7f (svn r4034) merged 3618:3971 for video/cocoa_v.m (major speedup for PPC fullscreen fix) 2006-03-22 21:11:05 +00:00
matthijs
5d0ed8fab8 (svn r4032) -Backport from trunk (3507):
- Fix: [Makefile] Make sure the ICON_DIR gets created before copying files there.
  - Fix: Fix small syntactic error in the manpage.
2006-03-22 21:04:13 +00:00
Darkvater
ed7df6e2bf (svn r4031) - Backport from trunk (r4030):
- [win32] Change compiler settings to use the multithreaded CRT. This prevents
  certain crashes on multi-threaded machines.
2006-03-22 20:46:07 +00:00
Darkvater
7cf9e0d8ca (svn r4029) - Backport from trunk (r4023):
Fix: [ 1453646 NPF ] Road vehicles planning through the back of depots and stations.
2006-03-22 20:33:30 +00:00
celestar
85141929a8 (svn r4018) -Backport from trunk (4001:4002):
Add length parameter to FiosMakeSavegameName() and use this function for creating the full path instead of home-brewn snprintf.
	Use the title of a savegame in the saveload dialog-editbox. This gets rid of the '.sav' appended to each game as well as properly showing UTF-8 saves when this is implemented. Also don't change the text if the save has failed.
2006-03-22 11:26:08 +00:00
celestar
f4d5c1b01b (svn r4017) -Backport from trunk (3999): Change the order of DestroyWindow and ChangeDisplay. On some machines a sizechange messagequeue is handled before sending WM_DISPLAYCHANGE resulting in an improper resolution written to the configuration file when exiting from fullscreen. (Frostregen) 2006-03-22 11:24:27 +00:00
celestar
31d6286cb4 (svn r4016) -Backport from trunk (3998): When removing rail track from a tile where only X and Y pieces exist, explicitly update signals in both directions. 2006-03-22 11:23:22 +00:00
celestar
ca0a0cdbfd (svn r4015) -Backport from trunk: Default the patch-setting 'pause_on_join' to true. 2006-03-22 11:17:21 +00:00
celestar
04572ed7fe (svn r4014) -Backport from trunk: Slope and height information returned for some tile types is wrong 2006-03-22 11:13:20 +00:00
celestar
ebfef9683e (svn r4013) -Fix last commit. CheckTunnelInWay works differently from IsTunnelInWay :S 2006-03-22 11:11:52 +00:00
celestar
c05d3dd558 (svn r4012) -Backport from trunk (3992, 3995): Rewrote the code to determine whether a rail-tile can be terraformed.
Fixes a bug where you could terraform a tunnel (fixed by r3228, but reverted that one)
Fixes a bug introduced by r3228 which allowed steep rail tiles resulting in ... unwanted effects such as display artifacts.
2006-03-22 10:32:07 +00:00
Darkvater
6e029fe97d (svn r3978) - Change all STRING1's back to STRING in french.txt because these {STRINGn} are only applicable to english.txt. Fixup of r3973. Sorry. Backport of r3977 from trunk 2006-03-19 09:03:25 +00:00
belugas
26ed195319 (svn r3975) Update french translation, adding STRING1 where needed, as well as other omissions.
Thanks to Darkvater for this opportunity. 
No typo this time
2006-03-19 01:52:05 +00:00
Darkvater
7170357a10 (svn r3970) - FS#56 - [Crash] Missing glyph(s) in big-font. Added several missing glyphs for the big font. Backport of r3940 from trunk 2006-03-18 16:03:55 +00:00
Darkvater
e4bbd3b41c (svn r3969) - [ 1439907 ] Increase client list window width so at least most languages fit (wikipedian). Backport of r3933 from trunk 2006-03-18 16:03:04 +00:00
Darkvater
5ed5e6beed (svn r3968) - Update german and finnish languages. Backport of r3932, r3943 from trunk 2006-03-18 16:02:19 +00:00
Darkvater
5de94db9df (svn r3967) - Fix: Properly set back the owner of a crossing/road-under bridge after removing it. For crossings we can always use .m2 because it is already 0 when not owned by a town. Backport of r3876, r3893 from trunk 2006-03-18 16:00:02 +00:00
Darkvater
6b664a3ba0 (svn r3965) - [win32] Remove mapfile generation and generate a pdb file instead. This and the corresponding executable is enough to trace the source of a crash given by crash.txt by using WinDbg for example. Mapfiles are a bit deprecated in the newer VS environments.
- [win32] Show the revision in crash.txt and enable the button to show the crash text in the crash-window 
- Backport of r3871, r3872 from trunk
2006-03-18 15:51:04 +00:00
Darkvater
beee5698f9 (svn r3964) -Fix: [autoreplace]: (FS#67) autoreplacing trains now keep their tile length instead of their pixel length. Backport of r3811 from trunk 2006-03-18 15:49:00 +00:00
Darkvater
a86ec733a0 (svn r3963) Update debian packaging files to the ones used for releasing 0.4.5 (see os/debian/changelog for details).
Fix a small debconf issue which was in the 0.4.5 release. Backport of r3801 from trunk
2006-03-18 15:48:15 +00:00
Darkvater
37c1135d6b (svn r3962) -Fix: Mark the right tile as dirty. It's just a graphical glitch which happend in r1592. Backport of r3792 from trunk 2006-03-18 15:47:16 +00:00
Darkvater
923dee9bec (svn r3961) - Fix crash when resizing news history window. Backport of r3778 from trunk 2006-03-18 15:46:09 +00:00
Darkvater
1856976d8e (svn r3960) -Fix: Correctly implement minimum search, so road vehicles head twoards the closest station, not the last one in the list. Backport of r3751 from trunk 2006-03-18 15:45:23 +00:00
Darkvater
818a5a596f (svn r3959) -Fix: [FS#61] The tooltips for raising and lowering land buttons in the scenario editor are interchanged (Reported and fixed by lc). Backport of r3749 from trunk 2006-03-18 15:32:38 +00:00
Darkvater
2be4b388ec (svn r3958) Change HASBIT() to return 0/1 instead of 0/value of tested bit. Backport of r3747 from trunk 2006-03-18 15:31:34 +00:00
Darkvater
300aba48cc (svn r3957) -Fix: Correctly restore the roadside after roadworks are finished. Backport of r3680 from trunk 2006-03-18 15:29:24 +00:00
Darkvater
f470a87dea (svn r3956) - Fix: [Multistop] Check the status of the destination road stop instead of a station's first road stop. This only has effect with road vehicle queuing disabled. Backport of r3663, r3681 from trunk 2006-03-18 15:28:26 +00:00
Darkvater
4b938510a5 (svn r3955) - Fix: validate the setting of max_companies/spectators through the console. Backport of r3591, r3593 from trunk 2006-03-18 15:25:25 +00:00
Darkvater
7ddae93da8 (svn r3954) - Explicitly update v->first in TrainConsistChanged() if necessary, as this is far faster than brute forcing it later.
- When loading a game, call TrainConsistChanged() for each train head separately before updating images, as v->first is used extensively in GetTrainImage() for custom graphics. This gives a significant speed improvement on loading a game. 
- Rewrite GetFreeUnitNumber() so that only one loop of vehicles is required. Instead a list of used/unused numbers is created and the first unused number is chosen. This significantly improves performance in large games. 
- Improve game-load times. Backport of r3570-3572 from trunk
2006-03-18 15:22:27 +00:00
Darkvater
f75365fcf4 (svn r3953) - Grr, compile before you commit. Wrong merge of 3529/3553 in r3948 2006-03-18 15:19:30 +00:00
Darkvater
e89a98d296 (svn r3952) - Fix: On loading a game, GetPlayerRailtypes() didn't account for the fact that vehicles are introduced a year after their introduction date. This will also relieve possible (rare) network desyncs. Backport of r3565 from trunk 2006-03-18 15:17:57 +00:00
Darkvater
8157969b2b (svn r3951) - Restore plural forms of cargo types for several languages. Backport of r3560 from trunk. 2006-03-18 15:16:12 +00:00
Darkvater
b3ccef7045 (svn r3950) - Add directives to allow Visual Studio 2005 compilation. Backport of r3551 from trunk. 2006-03-18 15:14:34 +00:00
Darkvater
af0fb58264 (svn r3949) - Revert r3467, was total nonesense, my fault. Backport of r3532 from trunk 2006-03-18 15:13:35 +00:00
Darkvater
5f2e2ef32a (svn r3948) - Fix: [ 1415782 ] crash in string code with openbsd/zaurus; alignment issues (thanks Tron for the help). Backport of r3529, r3553 from trunk 2006-03-18 15:12:24 +00:00
Darkvater
8353c1260a (svn r3526) - Codechange: Add additional linker information to release builds to help figure out crashes more easily 2006-02-03 17:24:37 +00:00
Darkvater
8de919ce50 (svn r3519) - [0.4.5-Branch] backport changes from abused tags/ (nothing important) 2006-02-01 22:02:47 +00:00
Darkvater
f9cacc9f5c (svn r3518) - Add proper revision numbers for bugfixing branch 2006-02-01 21:53:12 +00:00
Darkvater
af3ac4954a (svn r3517) - Branch: added branch for stable 0.4.5 2006-02-01 21:47:10 +00:00
433 changed files with 71981 additions and 146504 deletions

7
BUGS
View File

@@ -1,7 +0,0 @@
/* $Id$ */
KNOWN BUGS / PROBLEMS:
Normal and elrail depots look the same. Use 'X' (transparent buildings)
to distinguish between them
Missing curors / icons for construction (currently using the conventional ones)

39
COPYING
View File

@@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -15,7 +15,7 @@ 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 Lesser General Public License instead.) You can apply it to
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
@@ -55,8 +55,8 @@ 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
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
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
@@ -168,7 +168,7 @@ 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
@@ -225,7 +225,7 @@ 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
@@ -255,7 +255,7 @@ 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
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
@@ -277,9 +277,9 @@ 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
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
@@ -303,9 +303,10 @@ the "copyright" line and a pointer to where the full notice is found.
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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.
@@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
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 Lesser General
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

289
Makefile
View File

@@ -58,6 +58,9 @@
# WITH_DIRECTMUSIC: enable DirectMusic MIDI support
# WITH_NETWORK: enable networking
# DEDICATED: allows compilation on UNIX without SDL. Useful for dedicated servers
# MAX_NUM_AUTOSAVES: sets the number of autosaves the games will make before starting
# to overwrite the old ones. If not set, the game will use 16.
# NOTE: assign a number, not a string of a number
#
# Paths:
# INSTALL: If not set, the game uses the directory of the binary to
@@ -101,8 +104,6 @@
# CYGWIN: build in Cygwin environment
# MINGW: build with MingW compiler, link with MingW libraries
#
# CUSTOM_FONTCONFIG: use a custom name/path to the libfontconfig library. Useful for static linking
#
# VERBOSE: show full compiler invocations instead of brief progress messages
#
# Special for crosscompiling there are some commands available:
@@ -112,6 +113,9 @@
# OTTD_PPC, OTTD_PPC970, OTTD_i386: compile for target architecture.
# Multiple flags can be used so OTTD_PPC:=1 OTTD_i386:=1 produces the same result as UNIVERSAL_BINARY
#
# JAGUAR: Crosscompiling for OSX 1.2.8 (codenamed Jaguar). Only works if OSX is defined too. Only works with GCC 4 or newer
# This can be changed to any PPC version of OSX by changing the ppc flags in Makefile.config
#
# ENDIAN_FORCE: forces the endian-check to give a certain result. Can be BE, LE or PREPROCESSOR.
# PREPROCESSOR is always used on all OSX targets and will make the preprocessor pick the right endian.
# this means that you don't have to think about endianess when compiling for OSX.
@@ -171,7 +175,7 @@ LANG_ERRORS = >/dev/null 2>&1
endif
ifdef OSX
-include os/macosx/Makefile.setup
-include os/MacOSX/Makefile.setup
endif
ifdef STATIC
@@ -234,18 +238,6 @@ $(error WITH_PNG can't be used when LIBPNG_CONFIG is not set. Edit Makefile.conf
endif
endif
ifdef WITH_FREETYPE
ifndef FREETYPE_CONFIG
$(error WITH_FREETYPE can't be used when FREETYPE_CONFIG is not set. Edit Makefile.config to correct this)
endif
endif
ifdef WITH_FONTCONFIG
ifndef FONTCONFIG_CONFIG
$(error WITH_FONTCONFIG can't be used when FONTOCNFIG_CONFIG is not set. Edit Makefile.config to correct this)
endif
endif
##############################################################################
#
# Compiler configuration
@@ -253,13 +245,9 @@ endif
# Executable file extension
ifdef WIN32
EXE=.exe
EXE=.exe
else
ifdef OS2
EXE=.exe
else
EXE=
endif
EXE=
endif
# Set output executable names
@@ -268,14 +256,7 @@ ENDIAN_CHECK=endian_check$(EXE)
STRGEN=strgen/strgen$(EXE)
OSXAPP="OpenTTD.app"
REV := 0.5.2
# define flag to use for -lrt (some OSes overwrites this later for compatibility)
ifndef LRT
ifndef MORPHOS
LRT:= -lrt
endif
endif
REV := 0.4.8
# MorphOS needs builddate
BUILDDATE=`date +%d.%m.%y`
@@ -285,49 +266,33 @@ ifndef WINDRES
WINDRES = windres
endif
# Check that CXX is defined. If not, then it's g++
ifndef CXX
CXX = g++
endif
# Check if CXX_HOST is defined. If not, it is CXX
ifndef CXX_HOST
CXX_HOST = $(CXX)
endif
# Check if we have a new target
ifndef CXX_TARGET
CXX_TARGET = $(CXX_HOST)
ifdef CC_TARGET
CC = $(CC_TARGET)
endif
# Check if CC_HOST is defined. If not, it is CC
ifndef CC_HOST
CC_HOST = $(CC)
endif
ifndef CFLAGS_HOST
CFLAGS_HOST = $(BASECFLAGS)
endif
# Check if we have a new target
ifndef CC_TARGET
CC_TARGET = $(CC_HOST)
endif
CC_VERSION = $(shell $(CC_TARGET) -dumpversion | cut -c 1,3)
CC_VERSION = $(shell $(CC) -dumpversion | cut -c 1,3)
# GNU make can only test for (in)equality
# this is a workaround to test for >=
ifeq ($(shell expr $(CC_VERSION) \>= 29), 1)
CFLAGS += -O -Wall -Wno-multichar -Wsign-compare -Wundef
CC_CFLAGS += -Wstrict-prototypes
CFLAGS += -O -Wall -Wno-multichar -Wsign-compare -Wstrict-prototypes -Wundef
CFLAGS += -Wwrite-strings -Wpointer-arith
endif
ifeq ($(shell expr $(CC_VERSION) \>= 30), 1)
CFLAGS += -W -Wno-unused-parameter
endif
ifeq ($(shell expr $(CC_VERSION) \>= 34), 1)
CC_CFLAGS += -Wdeclaration-after-statement -Wold-style-definition
CFLAGS += -Wdeclaration-after-statement -Wold-style-definition
endif
ifdef DEBUG
@@ -345,10 +310,6 @@ endif
ifdef PROFILE
CFLAGS += -pg
LDFLAGS += -pg
ifdef OSX
# Shark (Xcode's profiling tool) needs -g to relate CPU usage to line numbers in the source code
BASECFLAGS += -g
endif
endif
CDEFS=-DWITH_REV
@@ -359,16 +320,11 @@ ifndef PROFILE
ifndef MORPHOS
ifndef IRIX
# automatical strip breaks under morphos
ifdef OSX
# it appears that OSX can't handle automated stripping when mixing C and C++
# we will do it manually in the target OSX_STRIP
OSX_STRIP:=OSX_STRIP
else
BASECFLAGS += -s
LDFLAGS += -s
endif
endif
endif
endif
ifdef OSX
# these compilerflags makes the app run as fast as possible without making the app unstable. It works on G3 or newer
@@ -403,8 +359,6 @@ endif
ifdef MINGW
BASECFLAGS += -mno-cygwin
LDFLAGS += -mno-cygwin
# -lrt fails with MINGW, so we disable it
LRT:=
endif
endif
@@ -430,8 +384,7 @@ endif
ifdef MORPHOS
# -Wstrict-prototypes generates much noise because of system headers
# and it also uses 4-byte bools in the C++ ABI, so C bools need to be that size as well for YAPF to work
CFLAGS += -Wno-strict-prototypes -DFOUR_BYTE_BOOL
CFLAGS += -Wno-strict-prototypes
endif
ifdef SUNOS
@@ -449,14 +402,12 @@ endif
# SDL config
ifdef WITH_SDL
CDEFS += -DWITH_SDL
CCFLAGS_SDL := $(shell $(SDL_CONFIG) --cflags)
CFLAGS += $(CCFLAGS_SDL)
CFLAGS += $(shell $(SDL_CONFIG) --cflags)
ifdef STATIC
LDFLAGS_SDL := $(shell $(SDL_CONFIG) --static-libs)
LIBS += $(shell $(SDL_CONFIG) --static-libs)
else
LDFLAGS_SDL := $(shell $(SDL_CONFIG) --libs)
LIBS += $(shell $(SDL_CONFIG) --libs)
endif
LIBS += $(LDFLAGS_SDL)
endif
# zlib config
@@ -477,85 +428,33 @@ endif
# libpng config
ifdef WITH_PNG
CDEFS += -DWITH_PNG
CCFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --cppflags --I_opts)
CFLAGS += $(CCFLAGS_PNG)
CFLAGS += $(shell $(LIBPNG_CONFIG) --cppflags --I_opts)
# seems like older libpng versions are broken and need this
PNGCONFIG_FLAGS = --ldflags --libs
ifdef STATIC
ifdef OSX
# Seems like we need a tiny hack for OSX static to work
LDFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --prefix)/lib/libpng.a
LIBS += $(shell $(LIBPNG_CONFIG) --prefix)/lib/libpng.a
else
LDFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --static $(PNGCONFIG_FLAGS))
LIBS += $(shell $(LIBPNG_CONFIG) --static $(PNGCONFIG_FLAGS))
endif
else
LDFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --L_opts $(PNGCONFIG_FLAGS))
LIBS += $(shell $(LIBPNG_CONFIG) --L_opts $(PNGCONFIG_FLAGS))
endif
LIBS += $(LDFLAGS_PNG)
endif
# use std C++ lib:
LIBS += -lstdc++
ifndef MINGW
LIBS += -lc
endif
# freetype config
ifdef WITH_FREETYPE
CDEFS += -DWITH_FREETYPE
CCFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --cflags)
LDFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --libs)
ifdef STATIC
ifdef OSX
# OSX needs a special lib setting when linking statically
LDFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --prefix)/lib/libfreetype.a
ifndef JAGUAR
LIBS += -liconv
endif
endif
CFLAGS += $(CCFLAGS_FREETYPE)
LIBS += $(LDFLAGS_FREETYPE)
endif
# fontconfig config
ifdef WITH_FONTCONFIG
CDEFS += -DWITH_FONTCONFIG
CCFLAGS_FONTCONFIG := $(shell $(FONTCONFIG_CONFIG) --cflags)
LDFLAGS_FONTCONFIG := $(shell $(FONTCONFIG_CONFIG) --libs)
ifdef CUSTOM_FONTCONFIG
# To allow usage of non-default libs, such as absolute path to static libs
# not stored in Makefile.config
LDFLAGS_FONTCONFIG := $(CUSTOM_FONTCONFIG)
endif
CFLAGS += $(CCFLAGS_FONTCONFIG)
LIBS += $(LDFLAGS_FONTCONFIG)
endif
# iconv is enabled defaultly on OSX >= 10.3
ifdef OSX
WITH_ICONV=1
LIBS += -liconv
endif
ifdef WITH_ICONV
CDEFS += -DWITH_ICONV
ifdef WITH_ICONV_PATH
CFLAGS += -I$(WITH_ICONV_PATH)
endif
endif
# enables/disables assert()
ifdef DISABLE_ASSERTS
CFLAGS += -DNDEBUG
endif
ifdef NO_THREADS
CFLAGS += -DNO_THREADS
endif
# automatically disables asserts for release
ifdef RELEASE
ifndef ENABLE_ASSERTS
@@ -574,9 +473,6 @@ ifdef OSX
# set the endian flag for OSX, that can't fail
ENDIAN_FORCE:=PREPROCESSOR
# -lrt fails on OSX, so we disable it
LRT:=
ifndef DEDICATED
LIBS += -framework QuickTime
endif
@@ -605,6 +501,10 @@ CDEFS += -DMIDI_ARG=\"$(MIDI_ARG)\"
endif
endif
ifdef MAX_NUM_AUTOSAVES
CDEFS += -DMAX_NUM_AUTOSAVES=$(MAX_NUM_AUTOSAVES)
endif
ifdef WITH_NETWORK
CDEFS += -DENABLE_NETWORK
ifdef QNX
@@ -661,10 +561,6 @@ endif
ifdef USE_HOMEDIR
CDEFS += -DUSE_HOMEDIR
endif
ifdef ICON_DIR
CDEFS += -DICON_DIR=\"$(ICON_DIR_PREFIXED)/\"
endif
endif
##############################################################################
@@ -684,39 +580,27 @@ SRCS += aircraft_gui.c
SRCS += airport.c
SRCS += airport_gui.c
SRCS += aystar.c
SRCS += bmp.c
SRCS += bridge_gui.c
SRCS += bridge_map.c
SRCS += build_vehicle_gui.c
SRCS += callback_table.c
SRCS += clear_cmd.c
SRCS += command.c
SRCS += console.c
SRCS += console_cmds.c
SRCS += currency.c
SRCS += date.c
SRCS += debug.c
SRCS += dedicated.c
SRCS += depot.c
SRCS += depot_gui.c
SRCS += disaster_cmd.c
SRCS += dock_gui.c
SRCS += driver.c
SRCS += dummy_land.c
SRCS += economy.c
SRCS += elrail.c
SRCS += engine.c
SRCS += engine_gui.c
SRCS += fileio.c
SRCS += fios.c
SRCS += fontcache.c
SRCS += genworld.c
SRCS += genworld_gui.c
SRCS += gfx.c
SRCS += gfxinit.c
SRCS += graph_gui.c
SRCS += heightmap.c
SRCS += helpers.cpp
SRCS += industry_cmd.c
SRCS += industry_gui.c
SRCS += intro_gui.c
@@ -730,7 +614,6 @@ SRCS += misc.c
SRCS += misc_cmd.c
SRCS += misc_gui.c
SRCS += mixer.c
SRCS += music.c
SRCS += music_gui.c
SRCS += namegen.c
SRCS += network.c
@@ -741,25 +624,16 @@ SRCS += network_gui.c
SRCS += network_server.c
SRCS += network_udp.c
SRCS += newgrf.c
SRCS += newgrf_cargo.c
SRCS += newgrf_config.c
SRCS += newgrf_engine.c
SRCS += newgrf_gui.c
SRCS += newgrf_sound.c
SRCS += newgrf_spritegroup.c
SRCS += newgrf_station.c
SRCS += newgrf_text.c
SRCS += news_gui.c
SRCS += npf.c
SRCS += oldloader.c
SRCS += oldpool.c
SRCS += openttd.c
SRCS += order_cmd.c
SRCS += order_gui.c
SRCS += os_timer.c
SRCS += pathfind.c
SRCS += player_gui.c
SRCS += players.c
SRCS += pool.c
SRCS += queue.c
SRCS += rail.c
SRCS += rail_cmd.c
@@ -767,7 +641,6 @@ SRCS += rail_gui.c
SRCS += rev.c
SRCS += road_cmd.c
SRCS += road_gui.c
SRCS += road_map.c
SRCS += roadveh_cmd.c
SRCS += roadveh_gui.c
SRCS += saveload.c
@@ -779,16 +652,16 @@ SRCS += ship_gui.c
SRCS += signs.c
SRCS += smallmap_gui.c
SRCS += sound.c
SRCS += sprite.c
SRCS += spritecache.c
SRCS += station_cmd.c
SRCS += station_gui.c
SRCS += station_map.c
SRCS += station_newgrf.c
SRCS += string.c
SRCS += strings.c
SRCS += subsidy_gui.c
SRCS += terraform_gui.c
SRCS += texteff.c
SRCS += tgp.c
SRCS += thread.c
SRCS += tile.c
SRCS += town_cmd.c
@@ -810,11 +683,6 @@ SRCS += music/null_m.c
SRCS += sound/null_s.c
SRCS += video/dedicated_v.c
SRCS += video/null_v.c
SRCS += yapf/follow_track.cpp
SRCS += yapf/yapf_common.cpp
SRCS += yapf/yapf_rail.cpp
SRCS += yapf/yapf_road.cpp
SRCS += yapf/yapf_ship.cpp
# AI related files
SRCS += ai/ai.c
@@ -920,53 +788,55 @@ $(ENDIAN_CHECK): endian_check.c
$(Q)$(CC_HOST) $(CFLAGS_HOST) $(CDEFS) $< -o $@
ifndef MACOSX_BUILD
ifndef NATIVE_OSX
# OSX links in os/macosx/Makefile to handle universal binaries better
$(TTD): $(OBJS) $(MAKE_CONFIG)
@echo '===> Linking $@'
$(Q)$(CXX_TARGET) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@
$(Q)$(CC) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@
endif
$(STRGEN): strgen/strgen.c string.c endian_host.h table/control_codes.h
$(STRGEN): strgen/strgen.c endian_host.h
@echo '===> Compiling and Linking $@'
$(Q)$(CC_HOST) $(CFLAGS_HOST) -DSTRGEN strgen/strgen.c string.c -o $@
$(Q)$(CC_HOST) $(CFLAGS_HOST) $(CDEFS) $< -o $@
table/strings.h: lang/english.txt $(STRGEN)
@echo '===> Generating $@'
$(Q)$(STRGEN) -s lang -d table
$(Q)$(STRGEN)
lang/%.lng: lang/%.txt $(STRGEN) lang/english.txt
@echo '===> Compiling language $(*F)'
$(Q)$(STRGEN) $(STRGEN_FLAGS) -s lang -d lang $< $(LANG_ERRORS) || rm -f $@
$(Q)$(STRGEN) $(STRGEN_FLAGS) $< $(LANG_ERRORS) || rm -f $@
ifdef MORPHOS
release: all
$(Q)rm -fr "/t/openttd-$(REV)-morphos.lha"
$(Q)rm -fr "/t/openttd-$(RELEASE)-morphos.lha"
$(Q)mkdir -p "/t/"
$(Q)mkdir -p "/t/openttd-$(REV)-morphos"
$(Q)mkdir -p "/t/openttd-$(REV)-morphos/docs"
$(Q)mkdir -p "/t/openttd-$(REV)-morphos/data"
$(Q)mkdir -p "/t/openttd-$(REV)-morphos/lang"
$(Q)mkdir -p "/t/openttd-$(REV)-morphos/scenario"
$(Q)mkdir -p "/t/openttd-$(REV)-morphos/scenario/heightmap"
$(Q)cp -R $(TTD) "/t/openttd-$(REV)-morphos/"
$(Q)cp data/* "/t/openttd-$(REV)-morphos/data/"
$(Q)cp lang/*.lng "/t/openttd-$(REV)-morphos/lang/"
$(Q)-cp scenario/*.scn "/t/openttd-$(REV)-morphos/scenario/"
$(Q)-cp scenario/heightmap/* "/t/openttd-$(REV)-morphos/scenario/heightmap/"
$(Q)cp readme.txt "/t/openttd-$(REV)-morphos/docs/ReadMe"
$(Q)cp COPYING "/t/openttd-$(REV)-morphos/docs/"
$(Q)cp changelog.txt "/t/openttd-$(REV)-morphos/docs/ChangeLog"
$(Q)cp known-bugs.txt "/t/openttd-$(REV)-morphos/docs/known-bugs.txt"
$(Q)cp os/morphos/icons/openttd.info "/t/openttd-$(REV)-morphos/$(TTD).info"
$(Q)cp os/morphos/icons/docs.info "/t/openttd-$(REV)-morphos/docs.info"
$(Q)cp os/morphos/icons/drawer.info "/t/openttd-$(REV)-morphos.info"
$(Q)strip --strip-all --strip-unneeded --remove-section .comment "/t/openttd-$(REV)-morphos/$(TTD)"
$(Q)lha a -r "t:openttd-$(REV)-morphos.lha" "t:openttd-$(REV)-morphos"
$(Q)lha a "t:openttd-$(REV)-morphos.lha" "t:openttd-$(REV)-morphos.info"
$(Q)rm -fr "/t/openttd-$(REV)-morphos"
$(Q)rm -fr "/t/openttd-$(REV)-morphos.info"
$(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos"
$(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/docs"
$(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/data"
$(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/lang"
$(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/scenario"
$(Q)cp -R $(TTD) "/t/openttd-$(RELEASE)-morphos/"
$(Q)cp data/* "/t/openttd-$(RELEASE)-morphos/data/"
$(Q)cp lang/*.lng "/t/openttd-$(RELEASE)-morphos/lang/"
$(Q)cp scenario/* "/t/openttd-$(RELEASE)-morphos/scenario/"
$(Q)cp readme.txt "/t/openttd-$(RELEASE)-morphos/docs/ReadMe"
$(Q)cp docs/console.txt "/t/openttd-$(RELEASE)-morphos/docs/Console"
$(Q)cp COPYING "/t/openttd-$(RELEASE)-morphos/docs/"
$(Q)cp changelog.txt "/t/openttd-$(RELEASE)-morphos/docs/ChangeLog"
$(Q)cp known-bugs.txt "/t/openttd-$(RELEASE)-morphos/docs/known-bugs.txt"
$(Q)cp os/morphos/icons/openttd.info "/t/openttd-$(RELEASE)-morphos/$(TTD).info"
$(Q)cp os/morphos/icons/docs.info "/t/openttd-$(RELEASE)-morphos/docs.info"
$(Q)cp os/morphos/icons/drawer.info "/t/openttd-$(RELEASE)-morphos.info"
$(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/ReadMe.info"
$(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/Console.info"
$(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/COPYING.info"
$(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/ChangeLog.info"
$(Q)strip --strip-all --strip-unneeded --remove-section .comment "/t/openttd-$(RELEASE)-morphos/$(TTD)"
$(Q)lha a -r "t:openttd-$(RELEASE)-morphos.lha" "t:openttd-$(RELEASE)-morphos"
$(Q)lha a "t:openttd-$(RELEASE)-morphos.lha" "t:openttd-$(RELEASE)-morphos.info"
$(Q)rm -fr "/t/openttd-$(RELEASE)-morphos"
$(Q)rm -fr "/t/openttd-$(RELEASE)-morphos.info"
@echo "Release archive can be found in RAM:t/ now."
.PHONY: release
@@ -1024,21 +894,20 @@ endif
$(BINARY_DIR_INSTALL)
ifndef USE_HOMEDIR
mkdir -p $(PERSONAL_DIR)/scenario
mkdir -p $(PERSONAL_DIR)/scenario/heightmap
else
mkdir -p $(DATA_DIR_INSTALL)/scenario
mkdir -p $(DATA_DIR_INSTALL)/scenario/heightmap
endif
install $(TTD) $(BINARY_DIR_INSTALL)
install -m 644 lang/*.lng $(DATA_DIR_INSTALL)/lang
install -m 644 data/*.grf $(DATA_DIR_INSTALL)/data
install -m 644 data/opntitle.dat $(DATA_DIR_INSTALL)/data
# Generic menu icon
install -m 644 media/openttd.64.png $(ICON_DIR_INSTALL)
# Debian menu icon
install -m 644 media/openttd.32.xpm $(ICON_DIR_INSTALL)
# Window icon
install -m 644 media/openttd.32.bmp $(ICON_DIR_INSTALL)
ifndef USE_HOMEDIR
cp scenario/* $(PERSONAL_DIR)/scenario/
else
cp scenario/* $(DATA_DIR_INSTALL)/scenario/
endif
else #MorphOS
install:
$(error make install is not supported on MorphOS)
@@ -1077,7 +946,7 @@ depend:
@true # The include handles this automagically
# Introduce the dependencies
ifeq ($(findstring $(MAKECMDGOALS), clean info mrproper upgradeconf $(MAKE_CONFIG)),)
ifeq ($(findstring $(MAKECMDGOALS), clean info),)
-include $(DEPS)
endif
@@ -1087,30 +956,30 @@ endif
.deps/%.d: %.c $(MAKE_CONFIG) table/strings.h endian_target.h
@echo '===> DEP $<'
$(Q)$(CC_TARGET) $(CFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@
$(Q)$(CC) $(CFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@
.deps/%.d: %.cpp $(MAKE_CONFIG) table/strings.h endian_target.h
@echo '===> DEP $<'
$(Q)$(CXX_TARGET) $(CFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@
$(Q)$(CXX) $(CFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@
.deps/%.d: %.m $(MAKE_CONFIG) table/strings.h endian_target.h
@echo '===> DEP $<'
$(Q)$(CC_TARGET) $(OBJCFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@
$(Q)$(CC) $(OBJCFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@
ifndef MACOSX_BUILD
ifndef NATIVE_OSX
# OSX uses os/macosx/Makefile to compile files
%.o: %.c $(MAKE_CONFIG)
@echo '===> Compiling $<'
$(Q)$(CC_TARGET) $(CC_CFLAGS) $(CFLAGS) $(CDEFS) -c -o $@ $<
$(Q)$(CC) $(CFLAGS) $(CDEFS) -c -o $@ $<
%.o: %.cpp $(MAKE_CONFIG)
@echo '===> Compiling $<'
$(Q)$(CXX_TARGET) $(CFLAGS) $(CDEFS) -c -o $@ $<
$(Q)$(CXX) $(CFLAGS) $(CDEFS) -c -o $@ $<
%.o: %.m $(MAKE_CONFIG)
@echo '===> Compiling $<'
$(Q)$(CC_TARGET) $(CC_CFLAGS) $(CFLAGS) $(CDEFS) -c -o $@ $<
$(Q)$(CC) $(CFLAGS) $(CDEFS) -c -o $@ $<
endif
%.o: %.rc

10
StdAfx.c Normal file
View File

@@ -0,0 +1,10 @@
/* $Id$ */
// 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

74
ai/ai.c
View File

@@ -11,7 +11,7 @@
/**
* Dequeues commands put in the queue via AI_PutCommandInQueue.
*/
static void AI_DequeueCommands(PlayerID player)
static void AI_DequeueCommands(byte player)
{
AICommand *com, *entry_com;
@@ -30,12 +30,14 @@ static void AI_DequeueCommands(PlayerID player)
while ((com = entry_com) != NULL) {
_current_player = player;
/* Copy the DP back in place */
_cmd_text = com->text;
DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc);
/* Free item */
entry_com = com->next;
free(com->text);
if (com->text != NULL)
free(com->text);
free(com);
}
}
@@ -84,52 +86,57 @@ int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint pr
{
PlayerID old_lp;
int32 res = 0;
const char* tmp_cmdtext;
char *tmp_cmdtext = NULL;
/* If you enable DC_EXEC with DC_QUERY_COST you are a really strange
* person.. should we check for those funny jokes?
*/
/* The test already resets _cmd_text, so backup the pointer */
tmp_cmdtext = _cmd_text;
/* The test already free _cmd_text in most cases, so let's backup the string, else we have a problem ;) */
if (_cmd_text != NULL)
tmp_cmdtext = strdup(_cmd_text);
/* First, do a test-run to see if we can do this */
res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
res = DoCommandByTile(tile, p1, p2, flags & ~DC_EXEC, procc);
/* The command failed, or you didn't want to execute, or you are quering, return */
if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
if ((CmdFailed(res)) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
if (tmp_cmdtext != NULL)
free(tmp_cmdtext);
return res;
}
/* Restore _cmd_text */
_cmd_text = tmp_cmdtext;
/* Recover _cmd_text */
if (tmp_cmdtext != NULL)
_cmd_text = tmp_cmdtext;
/* If we did a DC_EXEC, and the command did not return an error, execute it
* over the network */
if (flags & DC_AUTO) procc |= CMD_AUTO;
if (flags & DC_NO_WATER) procc |= CMD_NO_WATER;
over the network */
if (flags & DC_AUTO) procc |= CMD_AUTO;
if (flags & DC_NO_WATER) procc |= CMD_NO_WATER;
/* NetworkSend_Command needs _local_player to be set correctly, so
* adjust it, and put it back right after the function */
adjust it, and put it back right after the function */
old_lp = _local_player;
_local_player = _current_player;
#ifdef ENABLE_NETWORK
/* Send the command */
if (_networking) {
if (_networking)
/* Network is easy, send it to his handler */
NetworkSend_Command(tile, p1, p2, procc, callback);
} else {
#else
{
else
#endif
/* If we execute BuildCommands directly in SP, we have a big problem with events
* so we need to delay is for 1 tick */
AI_PutCommandInQueue(_current_player, tile, p1, p2, procc, callback);
}
/* Set _local_player back */
_local_player = old_lp;
/* Free the temp _cmd_text var */
if (tmp_cmdtext != NULL)
free(tmp_cmdtext);
return res;
}
@@ -170,20 +177,30 @@ void AI_RunGameLoop(void)
/* Don't do anything if ai is disabled */
if (!_ai.enabled) return;
/* Don't do anything if we are a network-client, or the AI has been disabled */
if (_networking && (!_network_server || !_patches.ai_in_multiplayer)) return;
/* Don't do anything if we are a network-client
* (too bad when a client joins, he thinks the AIs are real, so it wants to control
* them.. this avoids that, while loading a network game in singleplayer, does make
* the AIs to continue ;))
*/
if (_networking && !_network_server && !_ai.network_client)
return;
/* New tick */
_ai.tick++;
/* Make sure the AI follows the difficulty rule.. */
assert(_opt.diff.competitor_speed <= 4);
if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) return;
if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0)
return;
/* Check for AI-client (so joining a network with an AI) */
if (!_networking || _network_server) {
if (_ai.network_client && _ai_player[_ai.network_playas].active) {
/* Run the script */
AI_DequeueCommands(_ai.network_playas);
AI_RunTick(_ai.network_playas);
} else if (!_networking || _network_server) {
/* Check if we want to run AIs (server or SP only) */
const Player* p;
Player *p;
FOR_ALL_PLAYERS(p) {
if (p->is_active && p->is_ai) {
@@ -205,7 +222,7 @@ void AI_RunGameLoop(void)
*/
void AI_StartNewAI(PlayerID player)
{
assert(IsValidPlayer(player));
assert(player < MAX_PLAYERS);
/* Called if a new AI is booted */
_ai_player[player].active = true;
@@ -216,6 +233,9 @@ void AI_StartNewAI(PlayerID player)
*/
void AI_PlayerDied(PlayerID player)
{
if (_ai.network_client && _ai.network_playas == player)
_ai.network_playas = OWNER_SPECTATOR;
/* Called if this AI died */
_ai_player[player].active = false;
}
@@ -225,12 +245,16 @@ void AI_PlayerDied(PlayerID player)
*/
void AI_Initialize(void)
{
bool ai_network_client = _ai.network_client;
/* First, make sure all AIs are DEAD! */
AI_Uninitialize();
memset(&_ai, 0, sizeof(_ai));
memset(&_ai_player, 0, sizeof(_ai_player));
_ai.network_client = ai_network_client;
_ai.network_playas = OWNER_SPECTATOR;
_ai.enabled = true;
}
@@ -239,7 +263,7 @@ void AI_Initialize(void)
*/
void AI_Uninitialize(void)
{
const Player* p;
Player* p;
FOR_ALL_PLAYERS(p) {
if (p->is_active && p->is_ai) AI_PlayerDied(p->index);

15
ai/ai.h
View File

@@ -4,7 +4,6 @@
#include "../functions.h"
#include "../network.h"
#include "../player.h"
#include "../command.h"
/* How DoCommands look like for an AI */
typedef struct AICommand {
@@ -22,16 +21,20 @@ typedef struct AICommand {
/* The struct for an AIScript Player */
typedef struct AIPlayer {
bool active; ///< Is this AI active?
AICommand *queue; ///< The commands that he has in his queue
AICommand *queue_tail; ///< The tail of this queue
bool active; //! Is this AI active?
AICommand *queue; //! The commands that he has in his queue
AICommand *queue_tail; //! The tail of this queue
} AIPlayer;
/* The struct to keep some data about the AI in general */
typedef struct AIStruct {
/* General */
bool enabled; ///< Is AI enabled?
uint tick; ///< The current tick (something like _frame_counter, only for AIs)
bool enabled; //! Is AI enabled?
uint tick; //! The current tick (something like _frame_counter, only for AIs)
/* For network-clients (a OpenTTD client who acts as an AI connected to a server) */
bool network_client; //! Are we a network_client?
uint8 network_playas; //! The current network player we are connected as
} AIStruct;
VARDEF AIStruct _ai;

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,6 @@
#include "../../debug.h"
#include "../../functions.h"
#include "../../map.h"
#include "../../road_map.h"
#include "../../tile.h"
#include "../../vehicle.h"
#include "../../command.h"
@@ -13,7 +12,6 @@
#include "../../engine.h"
#include "../../station.h"
#include "../../variables.h"
#include "../../bridge.h"
#include "../ai.h"
// Build HQ
@@ -68,15 +66,14 @@ int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag)
if (type2 != 0) break;
}
}
// There is only one bridge that can be built
// There is only one bridge that can be build..
if (type2 == 0 && type != 0) type2 = type;
// Now, simply, build the bridge!
if (p->ainew.tbt == AI_TRAIN) {
return AI_DoCommand(tile_a, tile_b, (0x00 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
} else {
return AI_DoCommand(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
}
if (p->ainew.tbt == AI_TRAIN)
return AI_DoCommand(tile_a, tile_b, (0<<8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
return AI_DoCommand(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
}
@@ -103,10 +100,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
// the first pieces and the last piece
if (part < 1) part = 1;
// When we are done, stop it
if (part >= PathFinderInfo->route_length - 1) {
PathFinderInfo->position = -2;
return 0;
}
if (part >= PathFinderInfo->route_length - 1) { PathFinderInfo->position = -2; return 0; }
if (PathFinderInfo->rail_or_road) {
@@ -116,7 +110,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
PathFinderInfo->position++;
// TODO: problems!
if (CmdFailed(cost)) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be built!");
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be build!");
return 0;
}
return cost;
@@ -127,7 +121,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
PathFinderInfo->position++;
// TODO: problems!
if (CmdFailed(cost)) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be built!");
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be build!");
return 0;
}
return cost;
@@ -135,7 +129,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
// Build normal rail
// Keep it doing till we go an other way
if (route_extra[part - 1] == 0 && route_extra[part] == 0) {
if (route_extra[part-1] == 0 && route_extra[part] == 0) {
while (route_extra[part] == 0) {
// Get the current direction
dir = AiNew_GetRailDirection(route[part-1], route[part], route[part+1]);
@@ -166,7 +160,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
PathFinderInfo->position++;
// TODO: problems!
if (CmdFailed(cost)) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be built!");
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be build!");
return 0;
}
return cost;
@@ -177,7 +171,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
PathFinderInfo->position++;
// TODO: problems!
if (CmdFailed(cost)) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be built!");
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be build!");
return 0;
}
return cost;
@@ -201,7 +195,7 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
// Currently, we ignore CMD_ERRORs!
if (CmdFailed(res) && flag == DC_EXEC && !IsTileType(route[part], MP_STREET) && !EnsureNoVehicle(route[part])) {
// Problem.. let's just abort it all!
DEBUG(ai,0)("Darn, the route could not be built.. aborting!");
DEBUG(ai,0)("Darn, the route could not be builded.. aborting!");
p->ainew.state = AI_STATE_NOTHING;
return 0;
}
@@ -225,46 +219,29 @@ int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte fla
// This functions tries to find the best vehicle for this type of cargo
// It returns INVALID_ENGINE if not suitable engine is found
EngineID AiNew_PickVehicle(Player *p)
// It returns vehicle_id or -1 if not found
int AiNew_PickVehicle(Player *p)
{
if (p->ainew.tbt == AI_TRAIN) {
// Not supported yet
return INVALID_ENGINE;
return -1;
} else {
EngineID best_veh_index = INVALID_ENGINE;
int32 best_veh_rating = 0;
EngineID start = ROAD_ENGINES_INDEX;
EngineID end = ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES;
EngineID i;
/* Loop through all road vehicles */
for (i = start; i != end; i++) {
const RoadVehicleInfo *rvi = RoadVehInfo(i);
const Engine* e = GetEngine(i);
int32 rating;
int32 ret;
/* Skip vehicles which can't take our cargo type */
if (rvi->cargo_type != p->ainew.cargo && !CanRefitTo(i, p->ainew.cargo)) continue;
int start, count, i, ret = CMD_ERROR;
start = _cargoc.ai_roadveh_start[p->ainew.cargo];
count = _cargoc.ai_roadveh_count[p->ainew.cargo];
// Let's check it backwards.. we simply want to best engine available..
for (i = start + count - 1; i >= start; i--) {
// Is it availiable?
// Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY
if (!HASBIT(e->player_avail, _current_player) || e->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
/* Rate and compare the engine by speed & capacity */
rating = rvi->max_speed * rvi->capacity;
if (rating <= best_veh_rating) continue;
if (!HASBIT(GetEngine(i)->player_avail, _current_player) || GetEngine(i)->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
// Can we build it?
ret = AI_DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH);
if (CmdFailed(ret)) continue;
best_veh_rating = rating;
best_veh_index = i;
if (!CmdFailed(ret)) break;
}
return best_veh_index;
// We did not find a vehicle :(
if (CmdFailed(ret)) return -1;
return i;
}
}
@@ -276,15 +253,6 @@ void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
if (success) {
p->ainew.state = AI_STATE_GIVE_ORDERS;
p->ainew.veh_id = _new_vehicle_id;
if (GetVehicle(p->ainew.veh_id)->cargo_type != p->ainew.cargo) {
/* Cargo type doesn't match, so refit it */
if (CmdFailed(DoCommand(tile, p->ainew.veh_id, p->ainew.cargo, DC_EXEC, CMD_REFIT_ROAD_VEH))) {
/* Refit failed, so sell the vehicle */
DoCommand(tile, p->ainew.veh_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
p->ainew.state = AI_STATE_NOTHING;
}
}
} else {
/* XXX this should be handled more gracefully */
p->ainew.state = AI_STATE_NOTHING;
@@ -295,9 +263,9 @@ void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
// Builds the best vehicle possible
int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag)
{
EngineID i = AiNew_PickVehicle(p);
int i = AiNew_PickVehicle(p);
if (i == -1) return CMD_ERROR;
if (i == INVALID_ENGINE) return CMD_ERROR;
if (p->ainew.tbt == AI_TRAIN) return CMD_ERROR;
if (flag & DC_EXEC) {
@@ -307,18 +275,18 @@ int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag)
}
}
int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag)
int AiNew_Build_Depot(Player *p, TileIndex tile, byte direction, byte flag)
{
static const byte _roadbits_by_dir[4] = {2,1,8,4};
int ret, ret2;
if (p->ainew.tbt == AI_TRAIN) {
if (p->ainew.tbt == AI_TRAIN)
return AI_DoCommand(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT);
} else {
ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
if (CmdFailed(ret)) return ret;
// Try to build the road from the depot
ret2 = AI_DoCommand(tile + TileOffsByDiagDir(direction), DiagDirToRoadBits(ReverseDiagDir(direction)), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
// If it fails, ignore it..
if (CmdFailed(ret2)) return ret;
return ret + ret2;
}
ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
if (CmdFailed(ret)) return ret;
// Try to build the road from the depot
ret2 = AI_DoCommand(tile + TileOffsByDir(direction), _roadbits_by_dir[direction], 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
// If it fails, ignore it..
if (CmdFailed(ret2)) return ret;
return ret + ret2;
}

View File

@@ -2,7 +2,6 @@
#include "../../stdafx.h"
#include "../../openttd.h"
#include "../../bridge_map.h"
#include "../../debug.h"
#include "../../functions.h"
#include "../../map.h"
@@ -10,8 +9,7 @@
#include "../../command.h"
#include "trolly.h"
#include "../../depot.h"
#include "../../tunnel_map.h"
#include "../../bridge.h"
#include "../../variables.h"
#include "../ai.h"
#define TEST_STATION_NO_DIR 0xFF
@@ -44,8 +42,10 @@ static bool IsRoad(TileIndex tile)
// MP_STREET, but not a road depot?
(IsTileType(tile, MP_STREET) && !IsTileDepotType(tile, TRANSPORT_ROAD)) ||
(IsTileType(tile, MP_TUNNELBRIDGE) && (
(IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_ROAD) ||
(IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_ROAD)
// road tunnel?
((_m[tile].m5 & 0x80) == 0 && (_m[tile].m5 & 0x4) == 0x4) ||
// road bridge?
((_m[tile].m5 & 0x80) != 0 && (_m[tile].m5 & 0x2) == 0x2)
));
}
@@ -57,8 +57,7 @@ static bool IsRoad(TileIndex tile)
// Check if the current tile is in our end-area
static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current)
{
const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target;
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
// It is not allowed to have a station on the end of a bridge or tunnel ;)
if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
@@ -156,11 +155,9 @@ void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo
// Now we add all the starting tiles
for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
TileIndex tile = TileXY(x, y);
if (!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) continue;
if (!TestCanBuildStationHere(tile, TEST_STATION_NO_DIR)) continue;
start_node.node.tile = tile;
if (!(IsTileType(TileXY(x, y), MP_CLEAR) || IsTileType(TileXY(x, y), MP_TREES))) continue;
if (!TestCanBuildStationHere(TileXY(x, y), TEST_STATION_NO_DIR)) continue;
start_node.node.tile = TileXY(x, y);
aystar->addstart(aystar, &start_node.node, 0);
}
}
@@ -170,13 +167,12 @@ void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo
// The h-value, simple calculation
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
{
const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target;
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int r, r2;
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
// The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDiagDir(PathFinderInfo->end_direction));
r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDiagDir(PathFinderInfo->end_direction));
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDir(PathFinderInfo->end_direction));
r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDir(PathFinderInfo->end_direction));
} else {
// No direction, so just get the fastest route to the station
r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl);
@@ -225,7 +221,7 @@ static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *curr
// Go through all surrounding tiles and check if they are within the limits
for (i = 0; i < 4; i++) {
TileIndex ctile = current->path.node.tile; // Current tile
TileIndex atile = ctile + TileOffsByDiagDir(i); // Adjacent tile
TileIndex atile = ctile + TileOffsByDir(i); // Adjacent tile
if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 &&
TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) {
@@ -235,10 +231,12 @@ static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *curr
// If the next step is a bridge, we have to enter it the right way
if (!PathFinderInfo->rail_or_road && IsRoad(atile)) {
if (IsTileType(atile, MP_TUNNELBRIDGE)) {
if (IsTunnel(atile)) {
if (GetTunnelDirection(atile) != i) continue;
} else {
if ((_m[atile].m5 & 1U) != DiagDirToAxis(i)) continue;
// An existing bridge... let's test the direction ;)
if ((_m[atile].m5 & 1U) != (i & 1)) continue;
// This problem only is valid for tunnels:
// When the last tile was not yet a tunnel, check if we enter from the right side..
if ((_m[atile].m5 & 0x80) == 0) {
if (i != (_m[atile].m5 & 3U)) continue;
}
}
}
@@ -317,20 +315,22 @@ static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *curr
// Next step, check for bridges and tunnels
if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
TileInfo ti;
// First we get the dir from this tile and his parent
DiagDirection dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
int dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
// It means we can only walk with the track, so the bridge has to be in the same direction
TileIndex tile = current->path.node.tile;
TileIndex new_tile = tile;
Slope tileh = GetTileSlope(tile, NULL);
FindLandscapeHeightByTile(&ti, tile);
// Bridges can only be build on land that is not flat
// And if there is a road or rail blocking
if (tileh != SLOPE_FLAT ||
(PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_STREET)) ||
(!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_RAILWAY))) {
if (ti.tileh != 0 ||
(PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDir(dir), MP_STREET)) ||
(!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDir(dir), MP_RAILWAY))) {
for (;;) {
new_tile += TileOffsByDiagDir(dir);
new_tile += TileOffsByDir(dir);
// Precheck, is the length allowed?
if (!CheckBridge_Stuff(0, GetBridgeLength(tile, new_tile))) break;
@@ -351,16 +351,16 @@ static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *curr
}
// Next, check for tunnels!
// Tunnels can only be built on slopes corresponding to the direction
// Tunnels can only be build with tileh of 3, 6, 9 or 12, depending on the direction
// For now, we check both sides for this tile.. terraforming gives fuzzy result
if ((dir == DIAGDIR_NE && tileh == SLOPE_NE) ||
(dir == DIAGDIR_SE && tileh == SLOPE_SE) ||
(dir == DIAGDIR_SW && tileh == SLOPE_SW) ||
(dir == DIAGDIR_NW && tileh == SLOPE_NW)) {
if ((dir == 0 && ti.tileh == 12) ||
(dir == 1 && ti.tileh == 6) ||
(dir == 2 && ti.tileh == 3) ||
(dir == 3 && ti.tileh == 9)) {
// Now simply check if a tunnel can be build
ret = AI_DoCommand(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
tileh = GetTileSlope(_build_tunnel_endtile, NULL);
if (!CmdFailed(ret) && (tileh == SLOPE_SW || tileh == SLOPE_SE || tileh == SLOPE_NW || tileh == SLOPE_NE)) {
FindLandscapeHeightByTile(&ti, _build_tunnel_endtile);
if (!CmdFailed(ret) && (ti.tileh == 3 || ti.tileh == 6 || ti.tileh == 9 || ti.tileh == 12)) {
aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
aystar->neighbours[aystar->num_neighbours++].direction = 0;
@@ -370,9 +370,9 @@ static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *curr
}
extern uint GetRailFoundation(Slope tileh, TrackBits bits); // XXX function declaration in .c
extern uint GetRoadFoundation(Slope tileh, uint bits); // XXX function declaration in .c
extern uint GetBridgeFoundation(Slope tileh, Axis); // XXX function declaration in .c
extern uint GetRailFoundation(uint tileh, uint bits);
extern uint GetRoadFoundation(uint tileh, uint bits);
extern uint GetBridgeFoundation(uint tileh, byte direction);
enum {
BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
};
@@ -382,8 +382,11 @@ static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current,
{
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int r, res = 0;
Slope tileh = GetTileSlope(current->tile, NULL);
Slope parent_tileh = GetTileSlope(parent->path.node.tile, NULL);
TileInfo ti, parent_ti;
// Gather some information about the tile..
FindLandscapeHeightByTile(&ti, current->tile);
FindLandscapeHeightByTile(&parent_ti, parent->path.node.tile);
// Check if we hit the end-tile
if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) {
@@ -413,25 +416,24 @@ static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current,
// when there is a flat land all around, they are more expensive to build, and
// especially they essentially block the ability to connect or cross the road
// from one side.
if (parent_tileh != SLOPE_FLAT && parent->path.parent != NULL) {
if (parent_ti.tileh != 0 && parent->path.parent != NULL) {
// Skip if the tile was from a bridge or tunnel
if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
if (PathFinderInfo->rail_or_road) {
r = GetRailFoundation(parent_tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
r = GetRailFoundation(parent_ti.tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
// Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information..
if (r >= 15 || (r == 0 && HASBIT(BRIDGE_NO_FOUNDATION, tileh))) {
if (r >= 15 || (r == 0 && (BRIDGE_NO_FOUNDATION & (1 << ti.tileh)))) {
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
} else {
res += AI_PATHFINDER_FOUNDATION_PENALTY;
}
} else {
if (!IsRoad(parent->path.node.tile) || !IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE)) {
r = GetRoadFoundation(parent_tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
if (r >= 15 || r == 0) {
if (!(IsRoad(parent->path.node.tile) && IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE))) {
r = GetRoadFoundation(parent_ti.tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
if (r >= 15 || r == 0)
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
} else {
else
res += AI_PATHFINDER_FOUNDATION_PENALTY;
}
}
}
}
@@ -451,19 +453,17 @@ static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current,
res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
// Check if we are going up or down, first for the starting point
// In user_data[0] is at the 8th bit the direction
if (!HASBIT(BRIDGE_NO_FOUNDATION, parent_tileh)) {
if (GetBridgeFoundation(parent_tileh, (current->user_data[0] >> 8) & 1) < 15) {
if (!(BRIDGE_NO_FOUNDATION & (1 << parent_ti.tileh))) {
if (GetBridgeFoundation(parent_ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
}
// Second for the end point
if (!HASBIT(BRIDGE_NO_FOUNDATION, tileh)) {
if (GetBridgeFoundation(tileh, (current->user_data[0] >> 8) & 1) < 15) {
if (!(BRIDGE_NO_FOUNDATION & (1 << ti.tileh))) {
if (GetBridgeFoundation(ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
}
if (parent_tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
if (tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
if (parent_ti.tileh == 0) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
if (ti.tileh == 0) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
// To prevent the AI from taking the fastest way in tiles, but not the fastest way
@@ -473,11 +473,10 @@ static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current,
if (parent->path.parent != NULL &&
AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
// When road exists, we don't like turning, but its free, so don't be to piggy about it
if (IsRoad(parent->path.node.tile)) {
if (IsRoad(parent->path.node.tile))
res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
} else {
else
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
}
}
} else {
// For rail we have 1 exeption: diagonal rail..
@@ -486,7 +485,7 @@ static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current,
int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
// First, see if we are on diagonal path, that is better than straight path
if (dir1 > 1) res -= AI_PATHFINDER_DIAGONAL_BONUS;
if (dir1 > 1) { res -= AI_PATHFINDER_DIAGONAL_BONUS; }
// First see if they are different
if (dir1 != dir2) {

View File

@@ -16,20 +16,35 @@ int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
// 4 = dig down-left
// 5 = dig up-right
uint x1 = TileX(tile_a);
uint x2 = TileX(tile_b);
uint x3 = TileX(tile_c);
int x1, x2, x3;
int y1, y2, y3;
uint y1 = TileY(tile_a);
uint y2 = TileY(tile_b);
uint y3 = TileY(tile_c);
x1 = TileX(tile_a);
x2 = TileX(tile_b);
x3 = TileX(tile_c);
y1 = TileY(tile_a);
y2 = TileY(tile_b);
y3 = TileY(tile_c);
if (y1 == y2 && y2 == y3) return 0;
if (x1 == x2 && x2 == x3) return 1;
if (y2 > y1) return x2 > x3 ? 2 : 4;
if (x2 > x1) return y2 > y3 ? 2 : 5;
if (y1 > y2) return x2 > x3 ? 5 : 3;
if (x1 > x2) return y2 > y3 ? 4 : 3;
if (y2 > y1) {
if (x2 > x3) return 2;
else return 4;
}
if (x2 > x1) {
if (y2 > y3) return 2;
else return 5;
}
if (y1 > y2) {
if (x2 > x3) return 5;
else return 3;
}
if (x1 > x2) {
if (y2 > y3) return 4;
else return 3;
}
return 0;
}
@@ -64,22 +79,19 @@ int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
}
// Get's the direction between 2 tiles seen from tile_a
DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b)
int AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b)
{
if (TileY(tile_a) < TileY(tile_b)) return DIAGDIR_SE;
if (TileY(tile_a) > TileY(tile_b)) return DIAGDIR_NW;
if (TileX(tile_a) < TileX(tile_b)) return DIAGDIR_SW;
return DIAGDIR_NE;
if (TileY(tile_a) < TileY(tile_b)) return 1;
if (TileY(tile_a) > TileY(tile_b)) return 3;
if (TileX(tile_a) < TileX(tile_b)) return 2;
return 0;
}
// This functions looks up if this vehicle is special for this AI
// and returns his flag
uint AiNew_GetSpecialVehicleFlag(Player* p, Vehicle* v)
{
uint i;
for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
uint AiNew_GetSpecialVehicleFlag(Player *p, Vehicle *v) {
int i;
for (i=0;i<AI_MAX_SPECIAL_VEHICLES;i++) {
if (p->ainew.special_vehicles[i].veh_id == v->index) {
return p->ainew.special_vehicles[i].flag;
}
@@ -89,22 +101,16 @@ uint AiNew_GetSpecialVehicleFlag(Player* p, Vehicle* v)
return 0;
}
bool AiNew_SetSpecialVehicleFlag(Player* p, Vehicle* v, uint flag)
{
int new_id = -1;
uint i;
for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
bool AiNew_SetSpecialVehicleFlag(Player *p, Vehicle *v, uint flag) {
int i, new_id = -1;
for (i=0;i<AI_MAX_SPECIAL_VEHICLES;i++) {
if (p->ainew.special_vehicles[i].veh_id == v->index) {
p->ainew.special_vehicles[i].flag |= flag;
return true;
}
if (new_id == -1 &&
p->ainew.special_vehicles[i].veh_id == 0 &&
p->ainew.special_vehicles[i].flag == 0) {
if (new_id == -1 && p->ainew.special_vehicles[i].veh_id == 0 &&
p->ainew.special_vehicles[i].flag == 0)
new_id = i;
}
}
// Out of special_vehicle spots :s

View File

@@ -21,8 +21,6 @@
#include "../../openttd.h"
#include "../../debug.h"
#include "../../functions.h"
#include "../../road_map.h"
#include "../../station_map.h"
#include "../../table/strings.h"
#include "../../map.h"
#include "../../tile.h"
@@ -34,8 +32,6 @@
#include "../../engine.h"
#include "../../gui.h"
#include "../../depot.h"
#include "../../vehicle.h"
#include "../../date.h"
#include "../ai.h"
// This function is called after StartUp. It is the init of an AI
@@ -48,7 +44,9 @@ static void AiNew_State_FirstTime(Player *p)
assert(p->ainew.state == AI_STATE_FIRST_TIME);
// We first have to init some things
if (_current_player == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_IN_PROGRESS, 0, 0);
if (_current_player == 1 || _ai.network_client) {
ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_IN_PROGRESS, 0, 0);
}
// The PathFinder (AyStar)
// TODO: Maybe when an AI goes bankrupt, this is de-init
@@ -127,19 +125,16 @@ static void AiNew_State_WakeUp(Player *p)
} else if (c < 100 && !_patches.ai_disable_veh_roadveh) {
// Do we have any spots for road-vehicles left open?
if (GetFreeUnitNumber(VEH_Road) <= _patches.max_roadveh) {
if (c < 85) {
if (c < 85)
p->ainew.action = AI_ACTION_TRUCK_ROUTE;
} else {
else
p->ainew.action = AI_ACTION_BUS_ROUTE;
}
}
#if 0
} else if (c < 200 && !_patches.ai_disable_veh_train) {
}/* else if (c < 200 && !_patches.ai_disable_veh_train) {
if (GetFreeUnitNumber(VEH_Train) <= _patches.max_trains) {
p->ainew.action = AI_ACTION_TRAIN_ROUTE;
}
#endif
}
}*/
p->ainew.counter = 0;
}
@@ -157,6 +152,14 @@ static void AiNew_State_WakeUp(Player *p)
return;
}
if (_patches.ai_disable_veh_roadveh && (
p->ainew.action == AI_ACTION_BUS_ROUTE ||
p->ainew.action == AI_ACTION_TRUCK_ROUTE
)) {
p->ainew.action = AI_ACTION_NONE;
return;
}
if (p->ainew.action == AI_ACTION_REPAY_LOAN &&
money > AI_MINIMUM_LOAN_REPAY_MONEY) {
// We start repaying some money..
@@ -210,8 +213,8 @@ static void AiNew_State_ActionDone(Player *p)
static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
{
if (type == AI_CITY) {
const Town* t = GetTown(ic);
const Station* st;
Town *t = GetTown(ic);
Station *st;
uint count = 0;
int j = 0;
@@ -229,6 +232,8 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
// and sometimes it takes up to 4 months before the stats are corectly.
// This way we don't get 12 busstations in one city of 100 population ;)
FOR_ALL_STATIONS(st) {
// Is it an active station
if (st->xy == 0) continue;
// Do we own it?
if (st->owner == _current_player) {
// Are we talking busses?
@@ -270,8 +275,8 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
return true;
}
if (type == AI_INDUSTRY) {
const Industry* i = GetIndustry(ic);
const Station* st;
Industry *i = GetIndustry(ic);
Station *st;
int count = 0;
int j = 0;
@@ -279,7 +284,7 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
// No limits on delevering stations!
// Or for industry that does not give anything yet
if (i->produced_cargo[0] == CT_INVALID || i->total_production[0] == 0) return true;
if (i->produced_cargo[0] == 0xFF || i->total_production[0] == 0) return true;
if (i->total_production[0] - i->total_transported[0] < AI_CHECKCITY_NEEDED_CARGO) return false;
@@ -287,6 +292,9 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
// else we don't do it. This is done, because stat updates can be slow
// and sometimes it takes up to 4 months before the stats are corectly.
FOR_ALL_STATIONS(st) {
// Is it an active station
if (st->xy == 0) continue;
// Do we own it?
if (st->owner == _current_player) {
// Are we talking trucks?
@@ -302,7 +310,7 @@ static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type)
// we want to know if this station gets the same good. If so,
// we want to know its rating. If it is too high, we are not going
// to build there
if (i->produced_cargo[0] == CT_INVALID) continue;
if (i->produced_cargo[0] == 0xFF) continue;
// It does not take this cargo
if (!st->goods[i->produced_cargo[0]].last_speed) continue;
// Is it around our industry
@@ -376,11 +384,10 @@ static void AiNew_State_LocateRoute(Player *p)
if (p->ainew.from_ic == -1) {
if (p->ainew.temp == -1) {
// First, we pick a random spot to search from
if (p->ainew.from_type == AI_CITY) {
p->ainew.temp = AI_RandomRange(GetMaxTownIndex() + 1);
} else {
p->ainew.temp = AI_RandomRange(GetMaxIndustryIndex() + 1);
}
if (p->ainew.from_type == AI_CITY)
p->ainew.temp = AI_RandomRange(_total_towns);
else
p->ainew.temp = AI_RandomRange(_total_industries);
}
if (!AiNew_Check_City_or_Industry(p, p->ainew.temp, p->ainew.from_type)) {
@@ -389,9 +396,9 @@ static void AiNew_State_LocateRoute(Player *p)
// to try again
p->ainew.temp++;
if (p->ainew.from_type == AI_CITY) {
if (p->ainew.temp > GetMaxTownIndex()) p->ainew.temp = 0;
if (p->ainew.temp >= (int)_total_towns) p->ainew.temp = 0;
} else {
if (p->ainew.temp > GetMaxIndustryIndex()) p->ainew.temp = 0;
if (p->ainew.temp >= _total_industries) p->ainew.temp = 0;
}
// Don't do an attempt if we are trying the same id as the last time...
@@ -412,11 +419,10 @@ static void AiNew_State_LocateRoute(Player *p)
// Find a to-city
if (p->ainew.temp == -1) {
// First, we pick a random spot to search to
if (p->ainew.to_type == AI_CITY) {
p->ainew.temp = AI_RandomRange(GetMaxTownIndex() + 1);
} else {
p->ainew.temp = AI_RandomRange(GetMaxIndustryIndex() + 1);
}
if (p->ainew.to_type == AI_CITY)
p->ainew.temp = AI_RandomRange(_total_towns);
else
p->ainew.temp = AI_RandomRange(_total_industries);
}
// The same city is not allowed
@@ -424,30 +430,23 @@ static void AiNew_State_LocateRoute(Player *p)
if (p->ainew.temp != p->ainew.from_ic && AiNew_Check_City_or_Industry(p, p->ainew.temp, p->ainew.to_type)) {
// Maybe it is valid..
/* We need to know if they are not to far apart from eachother..
* We do that by checking how much cargo we have to move and how long the
* route is.
*/
// We need to know if they are not to far apart from eachother..
// We do that by checking how much cargo we have to move and how long the route
// is.
if (p->ainew.from_type == AI_CITY && p->ainew.tbt == AI_BUS) {
const Town* town_from = GetTown(p->ainew.from_ic);
const Town* town_temp = GetTown(p->ainew.temp);
uint distance = DistanceManhattan(town_from->xy, town_temp->xy);
int max_cargo;
max_cargo = town_from->max_pass + town_temp->max_pass;
max_cargo -= town_from->act_pass + town_temp->act_pass;
int max_cargo = GetTown(p->ainew.from_ic)->max_pass + GetTown(p->ainew.temp)->max_pass;
max_cargo -= GetTown(p->ainew.from_ic)->act_pass + GetTown(p->ainew.temp)->act_pass;
// max_cargo is now the amount of cargo we can move between the two cities
// If it is more than the distance, we allow it
if (distance <= max_cargo * AI_LOCATEROUTE_BUS_CARGO_DISTANCE) {
if (DistanceManhattan(GetTown(p->ainew.from_ic)->xy, GetTown(p->ainew.temp)->xy) <= max_cargo * AI_LOCATEROUTE_BUS_CARGO_DISTANCE) {
// We found a good city/industry, save the data of it
p->ainew.to_ic = p->ainew.temp;
p->ainew.state = AI_STATE_FIND_STATION;
DEBUG(ai,1)(
"[AiNew - LocateRoute] Found bus-route of %d tiles long (from %d to %d)",
distance,
DistanceManhattan(GetTown(p->ainew.from_ic)->xy, GetTown(p->ainew.temp)->xy),
p->ainew.from_ic,
p->ainew.temp
);
@@ -458,20 +457,17 @@ static void AiNew_State_LocateRoute(Player *p)
return;
}
} else if (p->ainew.tbt == AI_TRUCK) {
const Industry* ind_from = GetIndustry(p->ainew.from_ic);
const Industry* ind_temp = GetIndustry(p->ainew.temp);
bool found = false;
int max_cargo = 0;
uint i;
int i;
// TODO: in max_cargo, also check other cargo (beside [0])
// First we check if the from_ic produces cargo that this ic accepts
if (ind_from->produced_cargo[0] != CT_INVALID && ind_from->total_production[0] != 0) {
for (i = 0; i < lengthof(ind_temp->accepts_cargo); i++) {
if (ind_temp->accepts_cargo[i] == CT_INVALID) break;
if (ind_from->produced_cargo[0] == ind_temp->accepts_cargo[i]) {
// Found a compatible industry
max_cargo = ind_from->total_production[0] - ind_from->total_transported[0];
if (GetIndustry(p->ainew.from_ic)->produced_cargo[0] != 0xFF && GetIndustry(p->ainew.from_ic)->total_production[0] != 0) {
for (i=0;i<3;i++) {
if (GetIndustry(p->ainew.temp)->accepts_cargo[i] == 0xFF) break;
if (GetIndustry(p->ainew.from_ic)->produced_cargo[0] == GetIndustry(p->ainew.temp)->accepts_cargo[i]) {
// Found a compatbiel industry
max_cargo = GetIndustry(p->ainew.from_ic)->total_production[0] - GetIndustry(p->ainew.from_ic)->total_transported[0];
found = true;
p->ainew.from_deliver = true;
p->ainew.to_deliver = false;
@@ -479,14 +475,14 @@ static void AiNew_State_LocateRoute(Player *p)
}
}
}
if (!found && ind_temp->produced_cargo[0] != CT_INVALID && ind_temp->total_production[0] != 0) {
if (!found && GetIndustry(p->ainew.temp)->produced_cargo[0] != 0xFF && GetIndustry(p->ainew.temp)->total_production[0] != 0) {
// If not check if the current ic produces cargo that the from_ic accepts
for (i = 0; i < lengthof(ind_from->accepts_cargo); i++) {
if (ind_from->accepts_cargo[i] == CT_INVALID) break;
if (ind_temp->produced_cargo[0] == ind_from->accepts_cargo[i]) {
for (i=0;i<3;i++) {
if (GetIndustry(p->ainew.from_ic)->accepts_cargo[i] == 0xFF) break;
if (GetIndustry(p->ainew.temp)->produced_cargo[0] == GetIndustry(p->ainew.from_ic)->accepts_cargo[i]) {
// Found a compatbiel industry
found = true;
max_cargo = ind_temp->total_production[0] - ind_temp->total_transported[0];
max_cargo = GetIndustry(p->ainew.temp)->total_production[0] - GetIndustry(p->ainew.temp)->total_transported[0];
p->ainew.from_deliver = false;
p->ainew.to_deliver = true;
break;
@@ -496,21 +492,19 @@ static void AiNew_State_LocateRoute(Player *p)
if (found) {
// Yeah, they are compatible!!!
// Check the length against the amount of goods
uint distance = DistanceManhattan(ind_from->xy, ind_temp->xy);
if (distance > AI_LOCATEROUTE_TRUCK_MIN_DISTANCE &&
distance <= max_cargo * AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE) {
if (DistanceManhattan(GetIndustry(p->ainew.from_ic)->xy, GetIndustry(p->ainew.temp)->xy) > AI_LOCATEROUTE_TRUCK_MIN_DISTANCE &&
DistanceManhattan(GetIndustry(p->ainew.from_ic)->xy, GetIndustry(p->ainew.temp)->xy) <= max_cargo * AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE) {
p->ainew.to_ic = p->ainew.temp;
if (p->ainew.from_deliver) {
p->ainew.cargo = ind_from->produced_cargo[0];
p->ainew.cargo = GetIndustry(p->ainew.from_ic)->produced_cargo[0];
} else {
p->ainew.cargo = ind_temp->produced_cargo[0];
p->ainew.cargo = GetIndustry(p->ainew.temp)->produced_cargo[0];
}
p->ainew.state = AI_STATE_FIND_STATION;
DEBUG(ai,1)(
"[AiNew - LocateRoute] Found truck-route of %d tiles long (from %d to %d)",
distance,
DistanceManhattan(GetIndustry(p->ainew.from_ic)->xy, GetIndustry(p->ainew.temp)->xy),
p->ainew.from_ic,
p->ainew.temp
);
@@ -529,9 +523,9 @@ static void AiNew_State_LocateRoute(Player *p)
// to try again
p->ainew.temp++;
if (p->ainew.to_type == AI_CITY) {
if (p->ainew.temp > GetMaxTownIndex()) p->ainew.temp = 0;
if (p->ainew.temp >= (int)_total_towns) p->ainew.temp = 0;
} else {
if (p->ainew.temp > GetMaxIndustryIndex()) p->ainew.temp = 0;
if (p->ainew.temp >= _total_industries) p->ainew.temp = 0;
}
// Don't do an attempt if we are trying the same id as the last time...
@@ -553,7 +547,7 @@ static bool AiNew_CheckVehicleStation(Player *p, Station *st)
const Order *order;
FOR_VEHICLE_ORDERS(v, order) {
if (order->type == OT_GOTO_STATION && GetStation(order->dest) == st) {
if (order->type == OT_GOTO_STATION && GetStation(order->station) == st) {
// This vehicle has this city in its list
count++;
}
@@ -570,11 +564,11 @@ static void AiNew_State_FindStation(Player *p)
{
TileIndex tile;
Station *st;
int count = 0;
EngineID i;
int i, count = 0;
TileIndex new_tile = 0;
byte direction = 0;
Town *town = NULL;
Industry *industry = NULL;
assert(p->ainew.state == AI_STATE_FIND_STATION);
if (p->ainew.from_tile == 0) {
@@ -583,7 +577,8 @@ static void AiNew_State_FindStation(Player *p)
town = GetTown(p->ainew.from_ic);
tile = town->xy;
} else {
tile = GetIndustry(p->ainew.from_ic)->xy;
industry = GetIndustry(p->ainew.from_ic);
tile = industry->xy;
}
} else if (p->ainew.to_tile == 0) {
// Second we scan for a station in the to-city
@@ -591,7 +586,8 @@ static void AiNew_State_FindStation(Player *p)
town = GetTown(p->ainew.to_ic);
tile = town->xy;
} else {
tile = GetIndustry(p->ainew.to_ic)->xy;
industry = GetIndustry(p->ainew.to_ic);
tile = industry->xy;
}
} else {
// Unsupported request
@@ -607,25 +603,26 @@ static void AiNew_State_FindStation(Player *p)
i = AiNew_PickVehicle(p);
// Euhmz, this should not happen _EVER_
// Quit finding a route...
if (i == INVALID_ENGINE) {
p->ainew.state = AI_STATE_NOTHING;
return;
}
if (i == -1) { p->ainew.state = AI_STATE_NOTHING; return; }
FOR_ALL_STATIONS(st) {
if (st->owner == _current_player) {
if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) {
if (st->town == town) {
// Check how much cargo there is left in the station
if ((st->goods[p->ainew.cargo].waiting_acceptance & 0xFFF) > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) {
if (AiNew_CheckVehicleStation(p, st)) {
// We did found a station that was good enough!
new_tile = st->xy;
direction = GetRoadStopDir(st->xy);
break;
if (st->xy != 0) {
if (st->owner == _current_player) {
if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) {
if (st->town == town) {
// Check how much cargo there is left in the station
if ((st->goods[p->ainew.cargo].waiting_acceptance & 0xFFF) > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) {
if (AiNew_CheckVehicleStation(p, st)) {
// We did found a station that was good enough!
new_tile = st->xy;
// Cheap way to get the direction of the station...
// Bus stations save it as 0x47 .. 0x4A, so decrease it with 0x47, and tada!
direction = _m[st->xy].m5 - 0x47;
break;
}
}
count++;
}
count++;
}
}
}
@@ -670,7 +667,7 @@ static void AiNew_State_FindStation(Player *p)
}
}
// If i is still zero, we did not find anything
// If i is still zero, we did not found anything :(
if (i == 0) {
p->ainew.state = AI_STATE_NOTHING;
return;
@@ -680,7 +677,7 @@ static void AiNew_State_FindStation(Player *p)
best = 0;
new_tile = 0;
for (x = 0; x < i; x++) {
for (x=0;x<i;x++) {
if (found_best[x] > best ||
(found_best[x] == best && DistanceManhattan(tile, new_tile) > DistanceManhattan(tile, found_spot[x]))) {
new_tile = found_spot[x];
@@ -727,11 +724,9 @@ static void AiNew_State_FindPath(Player *p)
if (p->ainew.temp == -1) {
// Init path_info
if (p->ainew.from_tile == AI_STATION_RANGE) {
const Industry* i = GetIndustry(p->ainew.from_ic);
// For truck routes we take a range around the industry
p->ainew.path_info.start_tile_tl = i->xy - TileDiffXY(1, 1);
p->ainew.path_info.start_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
p->ainew.path_info.start_tile_tl = GetIndustry(p->ainew.from_ic)->xy - TileDiffXY(1, 1);
p->ainew.path_info.start_tile_br = GetIndustry(p->ainew.from_ic)->xy + TileDiffXY(GetIndustry(p->ainew.from_ic)->width, GetIndustry(p->ainew.from_ic)->height) + TileDiffXY(1, 1);
p->ainew.path_info.start_direction = p->ainew.from_direction;
} else {
p->ainew.path_info.start_tile_tl = p->ainew.from_tile;
@@ -740,10 +735,8 @@ static void AiNew_State_FindPath(Player *p)
}
if (p->ainew.to_tile == AI_STATION_RANGE) {
const Industry* i = GetIndustry(p->ainew.to_ic);
p->ainew.path_info.end_tile_tl = i->xy - TileDiffXY(1, 1);
p->ainew.path_info.end_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
p->ainew.path_info.end_tile_tl = GetIndustry(p->ainew.to_ic)->xy - TileDiffXY(1, 1);
p->ainew.path_info.end_tile_br = GetIndustry(p->ainew.to_ic)->xy + TileDiffXY(GetIndustry(p->ainew.to_ic)->width, GetIndustry(p->ainew.to_ic)->height) + TileDiffXY(1, 1);
p->ainew.path_info.end_direction = p->ainew.to_direction;
} else {
p->ainew.path_info.end_tile_tl = p->ainew.to_tile;
@@ -751,7 +744,10 @@ static void AiNew_State_FindPath(Player *p)
p->ainew.path_info.end_direction = p->ainew.to_direction;
}
p->ainew.path_info.rail_or_road = (p->ainew.tbt == AI_TRAIN);
if (p->ainew.tbt == AI_TRAIN)
p->ainew.path_info.rail_or_road = true;
else
p->ainew.path_info.rail_or_road = false;
// First, clean the pathfinder with our new begin and endpoints
clean_AyStar_AiPathFinder(p->ainew.pathfinder, &p->ainew.path_info);
@@ -761,21 +757,20 @@ static void AiNew_State_FindPath(Player *p)
// Start the pathfinder
r = p->ainew.pathfinder->main(p->ainew.pathfinder);
switch (r) {
case AYSTAR_NO_PATH:
DEBUG(ai,1)("[AiNew] PathFinder found no route!");
// Start all over again
p->ainew.state = AI_STATE_NOTHING;
break;
case AYSTAR_FOUND_END_NODE: // We found the end-point
p->ainew.temp = -1;
p->ainew.state = AI_STATE_FIND_DEPOT;
break;
// In any other case, we are still busy finding the route
default: break;
// If it return: no match, stop it...
if (r == AYSTAR_NO_PATH) {
DEBUG(ai,1)("[AiNew] PathFinder found no route!");
// Start all over again...
p->ainew.state = AI_STATE_NOTHING;
return;
}
if (r == AYSTAR_FOUND_END_NODE) {
// We found the end-point
p->ainew.temp = -1;
p->ainew.state = AI_STATE_FIND_DEPOT;
return;
}
// In any other case, we are still busy finding the route...
}
@@ -788,7 +783,7 @@ static void AiNew_State_FindDepot(Player *p)
// But first we walk through the route see if we can find a depot that is ours
// this keeps things nice ;)
int g, i, r;
DiagDirection j;
uint j;
TileIndex tile;
assert(p->ainew.state == AI_STATE_FIND_DEPOT);
@@ -797,16 +792,21 @@ static void AiNew_State_FindDepot(Player *p)
for (i=2;i<p->ainew.path_info.route_length-2;i++) {
tile = p->ainew.path_info.route[i];
for (j = 0; j < 4; j++) {
TileIndex t = tile + TileOffsByDiagDir(j);
if (IsTileType(t, MP_STREET) &&
GetRoadTileType(t) == ROAD_TILE_DEPOT &&
IsTileOwner(t, _current_player) &&
GetRoadDepotDirection(t) == ReverseDiagDir(j)) {
p->ainew.depot_tile = t;
p->ainew.depot_direction = ReverseDiagDir(j);
p->ainew.state = AI_STATE_VERIFY_ROUTE;
return;
if (IsTileType(tile + TileOffsByDir(j), MP_STREET)) {
// Its a street, test if it is a depot
if (_m[tile + TileOffsByDir(j)].m5 & 0x20) {
// We found a depot, is it ours? (TELL ME!!!)
if (IsTileOwner(tile + TileOffsByDir(j), _current_player)) {
// Now, is it pointing to the right direction.........
if (GB(_m[tile + TileOffsByDir(j)].m5, 0, 2) == (j ^ 2)) {
// Yeah!!!
p->ainew.depot_tile = tile + TileOffsByDir(j);
p->ainew.depot_direction = j ^ 2; // Reverse direction
p->ainew.state = AI_STATE_VERIFY_ROUTE;
return;
}
}
}
}
}
}
@@ -828,29 +828,29 @@ static void AiNew_State_FindDepot(Player *p)
tile = p->ainew.path_info.route[i];
for (j = 0; j < 4; j++) {
TileIndex t = tile + TileOffsByDiagDir(j);
// It may not be placed on the road/rail itself
// And because it is not build yet, we can't see it on the tile..
// So check the surrounding tiles :)
if (t == p->ainew.path_info.route[i - 1] ||
t == p->ainew.path_info.route[i + 1]) {
if (tile + TileOffsByDir(j) == p->ainew.path_info.route[i-1] ||
tile + TileOffsByDir(j) == p->ainew.path_info.route[i+1])
continue;
}
// Not around a bridge?
if (p->ainew.path_info.route_extra[i] != 0) continue;
if (IsTileType(tile, MP_TUNNELBRIDGE)) continue;
// Is the terrain clear?
if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) {
// If the current tile is on a slope then we do not allow this
if (GetTileSlope(tile, NULL) != SLOPE_FLAT) continue;
if (IsTileType(tile + TileOffsByDir(j), MP_CLEAR) ||
IsTileType(tile + TileOffsByDir(j), MP_TREES)) {
TileInfo ti;
FindLandscapeHeightByTile(&ti, tile);
// If the current tile is on a slope (tileh != 0) then we do not allow this
if (ti.tileh != 0) continue;
// Check if everything went okay..
r = AiNew_Build_Depot(p, t, ReverseDiagDir(j), 0);
r = AiNew_Build_Depot(p, tile + TileOffsByDir(j), j ^ 2, 0);
if (CmdFailed(r)) continue;
// Found a spot!
p->ainew.new_cost += r;
p->ainew.depot_tile = t;
p->ainew.depot_direction = ReverseDiagDir(j); // Reverse direction
p->ainew.depot_tile = tile + TileOffsByDir(j);
p->ainew.depot_direction = j ^ 2; // Reverse direction
p->ainew.state = AI_STATE_VERIFY_ROUTE;
return;
}
@@ -871,11 +871,10 @@ static int AiNew_HowManyVehicles(Player *p)
{
if (p->ainew.tbt == AI_BUS) {
// For bus-routes we look at the time before we are back in the station
EngineID i;
int length, tiles_a_day;
int i, length, tiles_a_day;
int amount;
i = AiNew_PickVehicle(p);
if (i == INVALID_ENGINE) return 0;
if (i == -1) return 0;
// Passenger run.. how long is the route?
length = p->ainew.path_info.route_length;
// Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
@@ -887,20 +886,18 @@ static int AiNew_HowManyVehicles(Player *p)
return amount;
} else if (p->ainew.tbt == AI_TRUCK) {
// For truck-routes we look at the cargo
EngineID i;
int length, amount, tiles_a_day;
int i, length, amount, tiles_a_day;
int max_cargo;
i = AiNew_PickVehicle(p);
if (i == INVALID_ENGINE) return 0;
if (i == -1) return 0;
// Passenger run.. how long is the route?
length = p->ainew.path_info.route_length;
// Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
if (p->ainew.from_deliver) {
if (p->ainew.from_deliver)
max_cargo = GetIndustry(p->ainew.from_ic)->total_production[0];
} else {
else
max_cargo = GetIndustry(p->ainew.to_ic)->total_production[0];
}
// This is because moving 60% is more than we can dream of!
max_cargo *= 0.6;
@@ -993,7 +990,7 @@ static void AiNew_State_BuildStation(Player *p)
p->ainew.state = AI_STATE_BUILD_PATH;
}
if (CmdFailed(res)) {
DEBUG(ai,0)("[AiNew - BuildStation] Strange but true... station can not be built!");
DEBUG(ai,0)("[AiNew - BuildStation] Strange but true... station can not be build!");
p->ainew.state = AI_STATE_NOTHING;
// If the first station _was_ build, destroy it
if (p->ainew.temp != 0)
@@ -1031,6 +1028,7 @@ static void AiNew_State_BuildPath(Player *p)
// This means we are done building!
if (p->ainew.tbt == AI_TRUCK && !_patches.roadveh_queue) {
static const byte _roadbits_by_dir[4] = {2,1,8,4};
// If they not queue, they have to go up and down to try again at a station...
// We don't want that, so try building some road left or right of the station
int dir1, dir2, dir3;
@@ -1038,14 +1036,14 @@ static void AiNew_State_BuildPath(Player *p)
int i, ret;
for (i=0;i<2;i++) {
if (i == 0) {
tile = p->ainew.from_tile + TileOffsByDiagDir(p->ainew.from_direction);
tile = p->ainew.from_tile + TileOffsByDir(p->ainew.from_direction);
dir1 = p->ainew.from_direction - 1;
if (dir1 < 0) dir1 = 3;
dir2 = p->ainew.from_direction + 1;
if (dir2 > 3) dir2 = 0;
dir3 = p->ainew.from_direction;
} else {
tile = p->ainew.to_tile + TileOffsByDiagDir(p->ainew.to_direction);
tile = p->ainew.to_tile + TileOffsByDir(p->ainew.to_direction);
dir1 = p->ainew.to_direction - 1;
if (dir1 < 0) dir1 = 3;
dir2 = p->ainew.to_direction + 1;
@@ -1053,9 +1051,9 @@ static void AiNew_State_BuildPath(Player *p)
dir3 = p->ainew.to_direction;
}
ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir1)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
ret = AI_DoCommand(tile, _roadbits_by_dir[dir1], 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
if (!CmdFailed(ret)) {
dir1 = TileOffsByDiagDir(dir1);
dir1 = TileOffsByDir(dir1);
if (IsTileType(tile + dir1, MP_CLEAR) || IsTileType(tile + dir1, MP_TREES)) {
ret = AI_DoCommand(tile+dir1, AiNew_GetRoadDirection(tile, tile+dir1, tile+dir1+dir1), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
if (!CmdFailed(ret)) {
@@ -1065,9 +1063,9 @@ static void AiNew_State_BuildPath(Player *p)
}
}
ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir2)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
ret = AI_DoCommand(tile, _roadbits_by_dir[dir2], 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
if (!CmdFailed(ret)) {
dir2 = TileOffsByDiagDir(dir2);
dir2 = TileOffsByDir(dir2);
if (IsTileType(tile + dir2, MP_CLEAR) || IsTileType(tile + dir2, MP_TREES)) {
ret = AI_DoCommand(tile+dir2, AiNew_GetRoadDirection(tile, tile+dir2, tile+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
if (!CmdFailed(ret)) {
@@ -1077,9 +1075,9 @@ static void AiNew_State_BuildPath(Player *p)
}
}
ret = AI_DoCommand(tile, DiagDirToRoadBits(dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
ret = AI_DoCommand(tile, _roadbits_by_dir[dir3^2], 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
if (!CmdFailed(ret)) {
dir3 = TileOffsByDiagDir(dir3);
dir3 = TileOffsByDir(dir3);
if (IsTileType(tile + dir3, MP_CLEAR) || IsTileType(tile + dir3, MP_TREES)) {
ret = AI_DoCommand(tile+dir3, AiNew_GetRoadDirection(tile, tile+dir3, tile+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
if (!CmdFailed(ret)) {
@@ -1104,9 +1102,9 @@ static void AiNew_State_BuildDepot(Player *p)
int res = 0;
assert(p->ainew.state == AI_STATE_BUILD_DEPOT);
if (IsTileType(p->ainew.depot_tile, MP_STREET) && GetRoadTileType(p->ainew.depot_tile) == ROAD_TILE_DEPOT) {
if (IsTileType(p->ainew.depot_tile, MP_STREET) && _m[p->ainew.depot_tile].m5 & 0x20) {
if (IsTileOwner(p->ainew.depot_tile, _current_player)) {
// The depot is already built
// The depot is already builded!
p->ainew.state = AI_STATE_BUILD_VEHICLE;
return;
} else {
@@ -1117,19 +1115,19 @@ static void AiNew_State_BuildDepot(Player *p)
}
// There is a bus on the tile we want to build road on... idle till he is gone! (BAD PERSON! :p)
if (!EnsureNoVehicle(p->ainew.depot_tile + TileOffsByDiagDir(p->ainew.depot_direction)))
if (!EnsureNoVehicle(p->ainew.depot_tile + TileOffsByDir(p->ainew.depot_direction)))
return;
res = AiNew_Build_Depot(p, p->ainew.depot_tile, p->ainew.depot_direction, DC_EXEC);
if (CmdFailed(res)) {
DEBUG(ai,0)("[AiNew - BuildDepot] Strange but true... depot can not be built!");
DEBUG(ai,0)("[AiNew - BuildDepot] Strange but true... depot can not be build!");
p->ainew.state = AI_STATE_NOTHING;
return;
}
p->ainew.state = AI_STATE_BUILD_VEHICLE;
p->ainew.idle = 10;
p->ainew.veh_main_id = INVALID_VEHICLE;
p->ainew.veh_main_id = (VehicleID)-1;
}
@@ -1176,7 +1174,7 @@ static void AiNew_State_GiveOrders(Player *p)
assert(p->ainew.state == AI_STATE_GIVE_ORDERS);
if (p->ainew.veh_main_id != INVALID_VEHICLE) {
if (p->ainew.veh_main_id != (VehicleID)-1) {
AI_DoCommand(0, p->ainew.veh_id + (p->ainew.veh_main_id << 16), 0, DC_EXEC, CMD_CLONE_ORDER);
p->ainew.state = AI_STATE_START_VEHICLE;
@@ -1190,14 +1188,14 @@ static void AiNew_State_GiveOrders(Player *p)
idx = 0;
order.type = OT_GOTO_DEPOT;
order.flags = OF_UNLOAD;
order.dest = GetDepotByTile(p->ainew.depot_tile)->index;
order.station = GetDepotByTile(p->ainew.depot_tile)->index;
AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
}
idx = 0;
order.type = OT_GOTO_STATION;
order.flags = 0;
order.dest = GetStationIndex(p->ainew.to_tile);
order.station = _m[p->ainew.to_tile].m2;
if (p->ainew.tbt == AI_TRUCK && p->ainew.to_deliver)
order.flags |= OF_FULL_LOAD;
AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
@@ -1205,7 +1203,7 @@ static void AiNew_State_GiveOrders(Player *p)
idx = 0;
order.type = OT_GOTO_STATION;
order.flags = 0;
order.dest = GetStationIndex(p->ainew.from_tile);
order.station = _m[p->ainew.from_tile].m2;
if (p->ainew.tbt == AI_TRUCK && p->ainew.from_deliver)
order.flags |= OF_FULL_LOAD;
AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER);
@@ -1236,11 +1234,9 @@ static void AiNew_State_StartVehicle(Player *p)
// Repays money
static void AiNew_State_RepayMoney(Player *p)
{
uint i;
for (i = 0; i < AI_LOAN_REPAY; i++) {
int i;
for (i=0;i<AI_LOAN_REPAY;i++)
AI_DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN);
}
p->ainew.state = AI_STATE_ACTION_DONE;
}
@@ -1293,6 +1289,7 @@ static void AiNew_State_CheckAllVehicles(Player *p)
Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (v->type == 0) continue;
if (v->owner != p->index) continue;
// Currently, we only know how to handle road-vehicles
if (v->type != VEH_Road) continue;

View File

@@ -246,7 +246,7 @@ void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo
// ai_shared.c
int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c);
int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c);
DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b);
int AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b);
bool AiNew_SetSpecialVehicleFlag(Player *p, Vehicle *v, uint flag);
uint AiNew_GetSpecialVehicleFlag(Player *p, Vehicle *v);
@@ -255,8 +255,8 @@ bool AiNew_Build_CompanyHQ(Player *p, TileIndex tile);
int AiNew_Build_Station(Player *p, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag);
int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag);
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag);
EngineID AiNew_PickVehicle(Player *p);
int AiNew_PickVehicle(Player *p);
int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag);
int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag);
int AiNew_Build_Depot(Player *p, TileIndex tile, byte direction, byte flag);
#endif /* AI_TROLLY_H */

View File

@@ -1,8 +1,5 @@
/* $Id$ */
#ifndef AIRCRAFT_H
#define AIRCRAFT_H
#include "station_map.h"
#include "vehicle.h"
@@ -17,10 +14,3 @@ static inline bool IsAircraftInHangarStopped(const Vehicle* v)
{
return IsAircraftInHangar(v) && v->vehstatus & VS_STOPPED;
}
uint16 AircraftDefaultCargoCapacity(CargoID cid, EngineID engine_type);
void CcCloneAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2);
void HandleAircraftEnterHangar(Vehicle *v);
#endif /* AIRCRAFT_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

480
airport.c
View File

@@ -7,44 +7,26 @@
#include "airport.h"
#include "macros.h"
#include "variables.h"
#include "airport_movement.h"
#include "date.h"
/* Uncomment this to print out a full report of the airport-structure
* You should either use
* - true: full-report, print out every state and choice with string-names
* OR
* - false: give a summarized report which only shows current and next position */
//#define DEBUG_AIRPORT false
static AirportFTAClass* CountryAirport;
static AirportFTAClass* CityAirport;
static AirportFTAClass* Oilrig;
static AirportFTAClass* Heliport;
static AirportFTAClass* MetropolitanAirport;
static AirportFTAClass* InternationalAirport;
static AirportFTAClass *CountryAirport;
static AirportFTAClass *CityAirport;
static AirportFTAClass *Oilrig;
static AirportFTAClass *Heliport;
static AirportFTAClass *MetropolitanAirport;
static AirportFTAClass *InternationalAirport;
static AirportFTAClass *CommuterAirport;
static AirportFTAClass *HeliDepot;
static AirportFTAClass *IntercontinentalAirport;
static AirportFTAClass *HeliStation;
static void AirportFTAClass_Constructor(AirportFTAClass *Airport,
const byte *terminals, const byte *helipads,
const byte entry_point, const byte acc_planes,
const AirportFTAbuildup *FA,
const TileIndexDiffC *depots, const byte nof_depots);
static void AirportFTAClass_Destructor(AirportFTAClass *Airport);
static void AirportFTAClass_Constructor(AirportFTAClass *apc,
const byte *terminals, const byte *helipads,
const byte entry_point, const byte acc_planes,
const AirportFTAbuildup *apFA,
const TileIndexDiffC *depots, const byte nof_depots,
uint size_x, uint size_y
);
static void AirportFTAClass_Destructor(AirportFTAClass *apc);
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA);
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA);
static byte AirportGetTerminalCount(const byte *terminals, byte *groups);
static byte AirportTestFTA(const AirportFTAClass *apc);
#ifdef DEBUG_AIRPORT
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report);
#endif /* DEBUG_AIRPORT */
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(void)
{
@@ -59,8 +41,7 @@ void InitializeAirports(void)
ALL,
_airport_fta_country,
_airport_depots_country,
lengthof(_airport_depots_country),
4, 3
lengthof(_airport_depots_country)
);
// city airport
@@ -74,8 +55,7 @@ void InitializeAirports(void)
ALL,
_airport_fta_city,
_airport_depots_city,
lengthof(_airport_depots_city),
6, 6
lengthof(_airport_depots_city)
);
// metropolitan airport
@@ -89,8 +69,7 @@ void InitializeAirports(void)
ALL,
_airport_fta_metropolitan,
_airport_depots_metropolitan,
lengthof(_airport_depots_metropolitan),
6, 6
lengthof(_airport_depots_metropolitan)
);
// international airport
@@ -104,23 +83,7 @@ void InitializeAirports(void)
ALL,
_airport_fta_international,
_airport_depots_international,
lengthof(_airport_depots_international),
7, 7
);
// intercontintental airport
IntercontinentalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
AirportFTAClass_Constructor(
IntercontinentalAirport,
_airport_terminal_intercontinental,
_airport_helipad_intercontinental,
43,
ALL,
_airport_fta_intercontinental,
_airport_depots_intercontinental,
lengthof(_airport_depots_intercontinental),
9,11
lengthof(_airport_depots_international)
);
// heliport, oilrig
@@ -134,57 +97,10 @@ void InitializeAirports(void)
HELICOPTERS_ONLY,
_airport_fta_heliport_oilrig,
NULL,
0,
1, 1
0
);
Oilrig = Heliport; // exactly the same structure for heliport/oilrig, so share state machine
// commuter airport
CommuterAirport = malloc(sizeof(AirportFTAClass));
AirportFTAClass_Constructor(
CommuterAirport,
_airport_terminal_commuter,
_airport_helipad_commuter,
22,
ALL,
_airport_fta_commuter,
_airport_depots_commuter,
lengthof(_airport_depots_commuter),
5,4
);
// helidepot airport
HeliDepot = malloc(sizeof(AirportFTAClass));
AirportFTAClass_Constructor(
HeliDepot,
NULL,
_airport_helipad_helidepot,
4,
HELICOPTERS_ONLY,
_airport_fta_helidepot,
_airport_depots_helidepot,
lengthof(_airport_depots_helidepot),
2,2
);
// helistation airport
HeliStation = malloc(sizeof(AirportFTAClass));
AirportFTAClass_Constructor(
HeliStation,
NULL,
_airport_helipad_helistation,
25,
HELICOPTERS_ONLY,
_airport_fta_helistation,
_airport_depots_helistation,
lengthof(_airport_depots_helistation),
4,2
);
}
void UnInitializeAirports(void)
@@ -194,199 +110,180 @@ void UnInitializeAirports(void)
AirportFTAClass_Destructor(Heliport);
AirportFTAClass_Destructor(MetropolitanAirport);
AirportFTAClass_Destructor(InternationalAirport);
AirportFTAClass_Destructor(CommuterAirport);
AirportFTAClass_Destructor(HeliDepot);
AirportFTAClass_Destructor(IntercontinentalAirport);
AirportFTAClass_Destructor(HeliStation);
}
static void AirportFTAClass_Constructor(AirportFTAClass *apc,
const byte *terminals, const byte *helipads,
const byte entry_point, const byte acc_planes,
const AirportFTAbuildup *apFA,
const TileIndexDiffC *depots, const byte nof_depots,
uint size_x, uint size_y
)
static void AirportFTAClass_Constructor(AirportFTAClass *Airport,
const byte *terminals, const byte *helipads,
const byte entry_point, const byte acc_planes,
const AirportFTAbuildup *FA,
const TileIndexDiffC *depots, const byte nof_depots)
{
byte nofterminals, nofhelipads;
byte nofterminalgroups, nofhelipadgroups;
byte nofterminalgroups = 0;
byte nofhelipadgroups = 0;
const byte * curr;
int i;
nofterminals = nofhelipads = 0;
apc->size_x = size_x;
apc->size_y = size_y;
//now we read the number of terminals we have
if (terminals != NULL) {
i = terminals[0];
nofterminalgroups = i;
curr = terminals;
while (i-- > 0) {
curr++;
assert(*curr != 0); //we don't want to have an empty group
nofterminals += *curr;
}
/* Set up the terminal and helipad count for an airport.
* TODO: If there are more than 10 terminals or 4 helipads, internal variables
* need to be changed, so don't allow that for now */
nofterminals = AirportGetTerminalCount(terminals, &nofterminalgroups);
if (nofterminals > MAX_TERMINALS) {
DEBUG(misc, 0) ("[Ap] Currently only maximum of %d terminals are supported (you wanted %d)", MAX_TERMINALS, nofterminals);
assert(nofterminals <= MAX_TERMINALS);
}
apc->terminals = terminals;
Airport->terminals = terminals;
//read helipads
if (helipads != NULL) {
i = helipads[0];
nofhelipadgroups = i;
curr = helipads;
while (i-- > 0) {
curr++;
assert(*curr != 0); //no empty groups please
nofhelipads += *curr;
}
nofhelipads = AirportGetTerminalCount(helipads, &nofhelipadgroups);
if (nofhelipads > MAX_HELIPADS) {
DEBUG(misc, 0) ("[Ap] Currently only maximum of %d helipads are supported (you wanted %d)", MAX_HELIPADS, nofhelipads);
assert(nofhelipads <= MAX_HELIPADS);
}
apc->helipads = helipads;
Airport->helipads = helipads;
/* Get the number of elements from the source table. We also double check this
* with the entry point which must be within bounds and use this information
* later on to build and validate the state machine */
apc->nofelements = AirportGetNofElements(apFA);
if (entry_point >= apc->nofelements) {
DEBUG(misc, 0) ("[Ap] Entry (%d) must be within the airport (maximum %d)", entry_point, apc->nofelements);
assert(entry_point < apc->nofelements);
}
// 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
apc->acc_planes = acc_planes;
apc->entry_point = entry_point;
apc->airport_depots = depots;
apc->nof_depots = nof_depots;
assert(nofterminals <= MAX_TERMINALS);
assert(nofhelipads <= MAX_HELIPADS);
/* Build the state machine itself */
AirportBuildAutomata(apc, apFA);
DEBUG(misc, 1) ("[Ap] #count %3d; #term %2d (%dgrp); #helipad %2d (%dgrp); entry %3d",
apc->nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, apc->entry_point);
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);
/* Test if everything went allright. This is only a rude static test checking
* the symantic correctness. By no means does passing the test mean that the
* airport is working correctly or will not deadlock for example */
{ byte ret = AirportTestFTA(apc);
if (ret != MAX_ELEMENTS) DEBUG(misc, 0) ("[Ap] ERROR with element: %d", ret - 1);
Airport->acc_planes = acc_planes;
Airport->entry_point = entry_point;
Airport->airport_depots = depots;
Airport->nof_depots = nof_depots;
// build the state machine
AirportBuildAutomata(Airport, FA);
DEBUG(misc, 1) ("#Elements %2d; #Terminals %2d in %d group(s); #Helipads %2d in %d group(s); Entry Point %d",
Airport->nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, Airport->entry_point
);
{
byte ret = AirportTestFTA(Airport);
if (ret != MAX_ELEMENTS) printf("ERROR with element: %d\n", ret - 1);
assert(ret == MAX_ELEMENTS);
}
#ifdef DEBUG_AIRPORT
AirportPrintOut(apc, DEBUG_AIRPORT);
#endif
// 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 *apc)
static void AirportFTAClass_Destructor(AirportFTAClass *Airport)
{
int i;
AirportFTA *current, *next;
for (i = 0; i < apc->nofelements; i++) {
current = apc->layout[i].next;
for (i = 0; i < Airport->nofelements; i++) {
current = Airport->layout[i].next_in_chain;
while (current != NULL) {
next = current->next;
next = current->next_in_chain;
free(current);
current = next;
};
}
free(apc->layout);
free(apc);
free(Airport->layout);
free(Airport);
}
/** Get the number of elements of a source Airport state automata
* Since it is actually just a big array of AirportFTA types, we only
* know one element from the other by differing 'position' identifiers */
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA)
static uint16 AirportGetNofElements(const AirportFTAbuildup *FA)
{
int i;
uint16 nofelements = 0;
int temp = apFA[0].position;
int temp = FA[0].position;
for (i = 0; i < MAX_ELEMENTS; i++) {
if (temp != apFA[i].position) {
if (temp != FA[i].position) {
nofelements++;
temp = apFA[i].position;
temp = FA[i].position;
}
if (apFA[i].position == MAX_ELEMENTS) break;
if (FA[i].position == MAX_ELEMENTS) break;
}
return nofelements;
}
/* We calculate the terminal/helipod count based on the data passed to us
* This data (terminals) contains an index as a first element as to how many
* groups there are, and then the number of terminals for each group */
static byte AirportGetTerminalCount(const byte *terminals, byte *groups)
{
byte i;
byte nof_terminals = 0;
*groups = 0;
if (terminals != NULL) {
i = terminals[0];
*groups = i;
while (i-- > 0) {
terminals++;
assert(*terminals != 0); // no empty groups please
nof_terminals += *terminals;
}
}
return nof_terminals;
}
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA)
static void AirportBuildAutomata(AirportFTAClass *Airport, const AirportFTAbuildup *FA)
{
AirportFTA *FAutomata;
AirportFTA *current;
AirportFTA *FAutomata = malloc(sizeof(AirportFTA) * apc->nofelements);
uint16 internalcounter = 0;
uint16 i;
uint16 internalcounter, i;
FAutomata = malloc(sizeof(AirportFTA) * Airport->nofelements);
Airport->layout = FAutomata;
internalcounter = 0;
apc->layout = FAutomata;
for (i = 0; i < apc->nofelements; i++) {
current = &apc->layout[i];
current->position = apFA[internalcounter].position;
current->heading = apFA[internalcounter].heading;
current->block = apFA[internalcounter].block;
current->next_position = apFA[internalcounter].next;
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 == apFA[internalcounter + 1].position) {
AirportFTA *newNode = malloc(sizeof(AirportFTA));
while (current->position == FA[internalcounter + 1].position) {
AirportFTA* newNode = malloc(sizeof(AirportFTA));
newNode->position = apFA[internalcounter + 1].position;
newNode->heading = apFA[internalcounter + 1].heading;
newNode->block = apFA[internalcounter + 1].block;
newNode->next_position = apFA[internalcounter + 1].next;
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 = newNode;
current = current->next;
current->next_in_chain = newNode;
current = current->next_in_chain;
internalcounter++;
} // while
current->next = NULL;
current->next_in_chain = NULL;
internalcounter++;
}
}
static byte AirportTestFTA(const AirportFTAClass *apc)
static byte AirportTestFTA(const AirportFTAClass *Airport)
{
byte position, i, next_position;
AirportFTA *current, *first;
next_position = 0;
byte position, i, next_element;
AirportFTA *temp;
next_element = 0;
for (i = 0; i < apc->nofelements; i++) {
position = apc->layout[i].position;
if (position != next_position) return i;
current = first = &apc->layout[i];
for (i = 0; i < Airport->nofelements; i++) {
position = Airport->layout[i].position;
if (position != next_element) return i;
temp = &Airport->layout[i];
for (; current != NULL; current = current->next) {
/* A heading must always be valid. The only exceptions are
* - multiple choices as start, identified by a special value of 255
* - terminal group which is identified by a special value of 255 */
if (current->heading > MAX_HEADINGS) {
if (current->heading != 255) return i;
if (current == first && current->next == NULL) return i;
if (current != first && current->next_position > apc->terminals[0]) return i;
}
/* If there is only one choice, it must be at the end */
if (current->heading == 0 && current->next != NULL) return i;
/* Obviously the elements of the linked list must have the same identifier */
if (position != current->position) return i;
/* A next position must be within bounds */
if (current->next_position >= apc->nofelements) return i;
}
next_position++;
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;
}
#ifdef DEBUG_AIRPORT
#if 0
static const char* const _airport_heading_strings[] = {
"TO_ALL",
"HANGAR",
@@ -407,83 +304,80 @@ static const char* const _airport_heading_strings[] = {
"ENDLANDING",
"HELILANDING",
"HELIENDLANDING",
"TERM7",
"TERM8",
"HELIPAD3",
"HELIPAD4",
"DUMMY" // extra heading for 255
"DUMMY" // extra heading for 255
};
static uint AirportBlockToString(uint32 block)
{
uint i = 0;
if (block & 0xffff0000) { block >>= 16; i += 16; }
if (block & 0x0000ff00) { block >>= 8; i += 8; }
if (block & 0x000000f0) { block >>= 4; i += 4; }
if (block & 0x0000000c) { block >>= 2; i += 2; }
if (block & 0x00000002) { i += 1; }
return i;
}
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report)
static void AirportPrintOut(const AirportFTAClass *Airport, const bool full_report)
{
AirportFTA *temp;
uint16 i;
byte heading;
if (!full_report) printf("(P = Current Position; NP = Next Position)\n");
for (i = 0; i < apc->nofelements; i++) {
AirportFTA *current = &apc->layout[i];
for (; current != NULL; current = current->next) {
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) {
byte heading = (current->heading == 255) ? MAX_HEADINGS + 1 : current->heading;
printf("\tPos:%2d NPos:%2d Heading:%15s Block:%2d\n", current->position,
current->next_position, _airport_heading_strings[heading],
AirportBlockToString(current->block));
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", current->position, current->next_position);
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;
}
#endif
const AirportFTAClass *GetAirport(const byte airport_type)
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) {
default: NOT_REACHED();
case AT_SMALL: return CountryAirport;
case AT_LARGE: return CityAirport;
case AT_METROPOLITAN: return MetropolitanAirport;
case AT_HELIPORT: return Heliport;
case AT_OILRIG: return Oilrig;
case AT_INTERNATIONAL: return InternationalAirport;
case AT_COMMUTER: return CommuterAirport;
case AT_HELIDEPOT: return HeliDepot;
case AT_INTERCON: return IntercontinentalAirport;
case AT_HELISTATION: return HeliStation;
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);
}
}
const AirportMovingData *GetAirportMovingData(byte airport_type, byte position)
{
assert(airport_type < lengthof(_airport_moving_datas));
assert(position < GetAirport(airport_type)->nofelements);
return &_airport_moving_datas[airport_type][position];
return Airport;
}
uint32 GetValidAirports(void)
{
uint32 bytemask = _avail_aircraft; /// sets the first 3 bytes, 0 - 2, @see AdjustAvailAircraft()
if (_cur_year >= 1980) SETBIT(bytemask, 3); // metropolitan airport
if (_cur_year >= 1990) SETBIT(bytemask, 4); // international airport
if (_cur_year >= 1983) SETBIT(bytemask, 5); // commuter airport
if (_cur_year >= 1976) SETBIT(bytemask, 6); // helidepot
if (_cur_year >= 2002) SETBIT(bytemask, 7); // intercontinental airport
if (_cur_year >= 1980) SETBIT(bytemask, 8); // helistation
// 1980-1-1 is --> 21915
// 1990-1-1 is --> 25568
if (_date >= 21915) SETBIT(bytemask, 3); // metropilitan airport 1980
if (_date >= 25568) SETBIT(bytemask, 4); // international airport 1990
return bytemask;
}

153
airport.h
View File

@@ -3,155 +3,52 @@
#ifndef AIRPORT_H
#define AIRPORT_H
enum {MAX_TERMINALS = 10};
enum {MAX_HELIPADS = 4};
enum {MAX_ELEMENTS = 255};
enum {MAX_HEADINGS = 22};
#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_COMMUTER = 5,
AT_HELIDEPOT = 6,
AT_INTERCON = 7,
AT_HELISTATION = 8,
AT_OILRIG = 15
AT_SMALL = 0,
AT_LARGE = 1,
AT_HELIPORT = 2,
AT_METROPOLITAN = 3,
AT_INTERNATIONAL = 4,
AT_OILRIG = 15
};
// do not change unless you change v->subtype too. This aligns perfectly with its current setting
enum {
AIRCRAFT_ONLY = 0,
ALL = 1,
HELICOPTERS_ONLY = 2,
AIRCRAFT_ONLY = 0,
ALL = 1,
HELICOPTERS_ONLY = 2
};
enum {
AMED_NOSPDCLAMP = 1 << 0,
AMED_TAKEOFF = 1 << 1,
AMED_SLOWTURN = 1 << 2,
AMED_LAND = 1 << 3,
AMED_EXACTPOS = 1 << 4,
AMED_BRAKE = 1 << 5,
AMED_HELI_RAISE = 1 << 6,
AMED_HELI_LOWER = 1 << 7,
};
/* Movement States on Airports (headings target) */
enum {
TO_ALL = 0,
HANGAR = 1,
TERM1 = 2,
TERM2 = 3,
TERM3 = 4,
TERM4 = 5,
TERM5 = 6,
TERM6 = 7,
HELIPAD1 = 8,
HELIPAD2 = 9,
TAKEOFF = 10,
STARTTAKEOFF = 11,
ENDTAKEOFF = 12,
HELITAKEOFF = 13,
FLYING = 14,
LANDING = 15,
ENDLANDING = 16,
HELILANDING = 17,
HELIENDLANDING = 18,
TERM7 = 19,
TERM8 = 20,
HELIPAD3 = 21,
HELIPAD4 = 22
};
// this maps the terminal to its corresponding state and block flag
// currently set for 10 terms, 4 helipads
static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22};
static const byte _airport_terminal_flag[] = {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25};
/* Movement Blocks on Airports */
// blocks (eg_airport_flags)
enum {
TERM1_block = 1 << 0,
TERM2_block = 1 << 1,
TERM3_block = 1 << 2,
TERM4_block = 1 << 3,
TERM5_block = 1 << 4,
TERM6_block = 1 << 5,
HELIPAD1_block = 1 << 6,
HELIPAD2_block = 1 << 7,
RUNWAY_IN_OUT_block = 1 << 8,
RUNWAY_IN_block = 1 << 8,
AIRPORT_BUSY_block = 1 << 8,
RUNWAY_OUT_block = 1 << 9,
TAXIWAY_BUSY_block = 1 << 10,
OUT_WAY_block = 1 << 11,
IN_WAY_block = 1 << 12,
AIRPORT_ENTRANCE_block = 1 << 13,
TERM_GROUP1_block = 1 << 14,
TERM_GROUP2_block = 1 << 15,
HANGAR2_AREA_block = 1 << 16,
TERM_GROUP2_ENTER1_block = 1 << 17,
TERM_GROUP2_ENTER2_block = 1 << 18,
TERM_GROUP2_EXIT1_block = 1 << 19,
TERM_GROUP2_EXIT2_block = 1 << 20,
PRE_HELIPAD_block = 1 << 21,
// blocks for new airports
TERM7_block = 1 << 22,
TERM8_block = 1 << 23,
TERM9_block = 1 << 24,
HELIPAD3_block = 1 << 24,
TERM10_block = 1 << 25,
HELIPAD4_block = 1 << 25,
HANGAR1_AREA_block = 1 << 26,
OUT_WAY2_block = 1 << 27,
IN_WAY2_block = 1 << 28,
RUNWAY_IN2_block = 1 << 29,
RUNWAY_OUT2_block = 1 << 10, // note re-uses TAXIWAY_BUSY
HELIPAD_GROUP_block = 1 << 13, // note re-uses AIRPORT_ENTRANCE
OUT_WAY_block2 = 1 << 31,
// end of new blocks
NOTHING_block = 1 << 30
};
typedef struct AirportMovingData {
int x,y;
byte flag;
byte direction;
} AirportMovingData;
// Finite sTate mAchine --> FTA
typedef struct AirportFTAClass {
byte nofelements; // number of positions the airport consists of
byte nofelements; // number of positions the airport consists of
const byte *terminals;
const byte *helipads;
byte entry_point; // when an airplane arrives at this airport, enter it at position entry_point
byte acc_planes; // accept airplanes or helicopters or both
const TileIndexDiffC *airport_depots; // gives the position of the depots on the airports
byte nof_depots; // number of depots this airport has
struct AirportFTA *layout; // state machine for airport
byte size_x;
byte size_y;
byte entry_point; // when an airplane arrives at this airport, enter it at position entry_point
byte acc_planes; // accept airplanes or helicopters or both
const TileIndexDiffC *airport_depots; // gives the position of the depots on the airports
byte nof_depots; // number of depots this airport has
struct AirportFTA *layout; // state machine for airport
} AirportFTAClass;
// internal structure used in openttd - Finite sTate mAchine --> FTA
typedef struct AirportFTA {
byte position; // the position that an airplane is at
byte next_position; // next position from this position
uint32 block; // 32 bit blocks (st->airport_flags), should be enough for the most complex airports
byte heading; // heading (current orders), guiding an airplane to its target on an airport
struct AirportFTA *next; // possible extra movement choices from this position
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);
void UnInitializeAirports(void);
const AirportFTAClass *GetAirport(const byte airport_type);
const AirportMovingData *GetAirportMovingData(byte airport_type, byte position);
const AirportFTAClass* GetAirport(const byte airport_type);
/** Get buildable airport bitmask.
* @return get all buildable airports at this given time, bitmasked.

View File

@@ -41,20 +41,14 @@ static void PlaceAir_DemolishArea(TileIndex tile)
}
enum {
ATW_AIRPORT = 3,
ATW_DEMOLISH = 4
};
static void BuildAirClick_Airport(Window *w)
{
if (HandlePlacePushButton(w, ATW_AIRPORT, SPR_CURSOR_AIRPORT, 1, PlaceAirport)) ShowBuildAirportPicker();
if (HandlePlacePushButton(w, 3, SPR_CURSOR_AIRPORT, 1, PlaceAirport)) ShowBuildAirportPicker();
}
static void BuildAirClick_Demolish(Window *w)
{
HandlePlacePushButton(w, ATW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceAir_DemolishArea);
HandlePlacePushButton(w, 4, ANIMCURSOR_DEMOLISH, 1, PlaceAir_DemolishArea);
}
static void BuildAirClick_Landscaping(Window *w)
@@ -77,12 +71,12 @@ static void BuildAirToolbWndProc(Window *w, WindowEvent *e)
break;
case WE_CLICK:
if (e->we.click.widget - 3 >= 0)
_build_air_button_proc[e->we.click.widget - 3](w);
if (e->click.widget - 3 >= 0)
_build_air_button_proc[e->click.widget - 3](w);
break;
case WE_KEYPRESS: {
switch (e->we.keypress.keycode) {
switch (e->keypress.keycode) {
case '1': BuildAirClick_Airport(w); break;
case '2': BuildAirClick_Demolish(w); break;
case 'l': BuildAirClick_Landscaping(w); break;
@@ -91,22 +85,23 @@ static void BuildAirToolbWndProc(Window *w, WindowEvent *e)
} break;
case WE_PLACE_OBJ:
_place_proc(e->we.place.tile);
_place_proc(e->place.tile);
break;
case WE_PLACE_DRAG:
VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
break;
case WE_PLACE_DRAG: {
VpSelectTilesWithMethod(e->place.pt.x, e->place.pt.y, e->place.userdata);
return;
}
case WE_PLACE_MOUSEUP:
if (e->we.place.pt.x != -1) {
DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
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:
RaiseWindowButtons(w);
UnclickWindowButtons(w);
SetWindowDirty(w);
w = FindWindowById(WC_BUILD_STATION, 0);
if (w != 0)
WP(w,def_d).close = true;
@@ -119,19 +114,19 @@ static void BuildAirToolbWndProc(Window *w, WindowEvent *e)
}
static const Widget _air_toolbar_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 73, 0, 13, STR_A000_AIRPORTS, STR_018C_WINDOW_TITLE_DRAG_THIS },
{ WWT_STICKYBOX, RESIZE_NONE, 7, 74, 85, 0, 13, 0x0, STR_STICKY_BUTTON },
{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 41, 14, 35, SPR_IMG_AIRPORT, STR_A01E_BUILD_AIRPORT },
{ WWT_IMGBTN, RESIZE_NONE, 7, 42, 63, 14, 35, SPR_IMG_DYNAMITE, STR_018D_DEMOLISH_BUILDINGS_ETC },
{ WWT_IMGBTN, RESIZE_NONE, 7, 64, 85, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP },
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 73, 0, 13, STR_A000_AIRPORTS, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_STICKYBOX, RESIZE_NONE, 7, 74, 85, 0, 13, 0x0, STR_STICKY_BUTTON},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 41, 14, 35, 0x2E8, STR_A01E_BUILD_AIRPORT},
{ WWT_PANEL, RESIZE_NONE, 7, 42, 63, 14, 35, 0x2BF, STR_018D_DEMOLISH_BUILDINGS_ETC},
{ WWT_PANEL, RESIZE_NONE, 7, 64, 85, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP},
{ WIDGETS_END},
};
static const WindowDesc _air_toolbar_desc = {
WDP_ALIGN_TBR, 22, 86, 36,
WC_BUILD_TOOLBAR, 0,
640-86, 22, 86, 36,
WC_BUILD_TOOLBAR,0,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
_air_toolbar_widgets,
BuildAirToolbWndProc
@@ -139,8 +134,7 @@ static const WindowDesc _air_toolbar_desc = {
void ShowBuildAirToolbar(void)
{
if (!IsValidPlayer(_current_player)) return;
if (_current_player == OWNER_SPECTATOR) return;
DeleteWindowById(WC_BUILD_TOOLBAR, 0);
AllocateWindowDescFront(&_air_toolbar_desc, 0);
if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
@@ -149,76 +143,59 @@ void ShowBuildAirToolbar(void)
static void BuildAirportPickerWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
case WE_CREATE:
SetWindowWidgetLoweredState(w, 16, !_station_show_coverage);
SetWindowWidgetLoweredState(w, 17, _station_show_coverage);
LowerWindowWidget(w, _selected_airport_type + 7);
break;
case WE_PAINT: {
int i; // airport enabling loop
int sel;
int rad = 4; // default catchment radious
uint32 avail_airports;
const AirportFTAClass *airport;
if (WP(w,def_d).close) return;
sel = _selected_airport_type;
avail_airports = GetValidAirports();
RaiseWindowWidget(w, _selected_airport_type + 7);
if (!HASBIT(avail_airports, 0) && _selected_airport_type == AT_SMALL) _selected_airport_type = AT_LARGE;
if (!HASBIT(avail_airports, 1) && _selected_airport_type == AT_LARGE) _selected_airport_type = AT_SMALL;
LowerWindowWidget(w, _selected_airport_type + 7);
if (!HASBIT(avail_airports, 0) && sel == AT_SMALL) sel = AT_LARGE;
if (!HASBIT(avail_airports, 1) && sel == AT_LARGE) sel = AT_SMALL;
/* 'Country Airport' starts at widget 7, and if its bit is set, it is
* available, so take its opposite value to set the disabled state.
* There are 9 buildable airports
* XXX TODO : all airports should be held in arrays, with all relevant data.
* This should be part of newgrf-airports, i suppose
*/
for (i = 0; i < 9; i++) SetWindowWidgetDisabledState(w, i + 7, !HASBIT(avail_airports, i));
/* 'Country Airport' starts at widget 3, and if its bit is set, it is
* available, so take its opposite value to set the disabled_state. There
* are only 5 available airports, so XOR with 0x1F (1 1111) */
w->disabled_state = (avail_airports ^ 0x1F) << 3;
// select default the coverage area to 'Off' (16)
airport = GetAirport(_selected_airport_type);
SetTileSelectSize(airport->size_x, airport->size_y);
_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 (_patches.modified_catchment) {
switch (_selected_airport_type) {
switch (sel) {
case AT_OILRIG: rad = CA_AIR_OILPAD; break;
case AT_HELIPORT: rad = CA_AIR_HELIPORT; break;
case AT_SMALL: rad = CA_AIR_SMALL; break;
case AT_LARGE: rad = CA_AIR_LARGE; break;
case AT_METROPOLITAN: rad = CA_AIR_METRO; break;
case AT_INTERNATIONAL: rad = CA_AIR_INTER; break;
case AT_COMMUTER: rad = CA_AIR_COMMUTER; break;
case AT_HELIDEPOT: rad = CA_AIR_HELIDEPOT; break;
case AT_INTERCON: rad = CA_AIR_INTERCON; break;
case AT_HELISTATION: rad = CA_AIR_HELISTATION; break;
}
}
if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
DrawWindowWidgets(w);
// strings such as 'Size' and 'Coverage Area'
// 'Coverage Area'
DrawStationCoverageAreaText(2, 206, (uint)-1, rad);
// 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, rad);
break;
}
case WE_CLICK: {
switch (e->we.click.widget) {
case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
RaiseWindowWidget(w, _selected_airport_type + 7);
_selected_airport_type = e->we.click.widget - 7;
LowerWindowWidget(w, _selected_airport_type + 7);
switch (e->click.widget) {
case 3: case 4: case 5: case 6: case 7:
_selected_airport_type = e->click.widget - 3;
SndPlayFx(SND_15_BEEP);
SetWindowDirty(w);
break;
case 16: case 17:
_station_show_coverage = e->we.click.widget - 16;
SetWindowWidgetLoweredState(w, 16, !_station_show_coverage);
SetWindowWidgetLoweredState(w, 17, _station_show_coverage);
case 8: case 9:
_station_show_coverage = e->click.widget - 8;
SndPlayFx(SND_15_BEEP);
SetWindowDirty(w);
break;
@@ -241,35 +218,22 @@ static void BuildAirportPickerWndProc(Window *w, WindowEvent *e)
}
static const Widget _build_airport_picker_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3001_AIRPORT_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 52, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 53, 89, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 90, 127, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 128, 177, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 178, 239, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 27, 38, STR_SMALL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 65, 76, STR_CITY_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 141, 152, STR_HELIPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 77, 88, STR_METRO_AIRPORT , STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 103, 114, STR_INTERNATIONAL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 39, 50, STR_COMMUTER_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 165, 176, STR_HELIDEPOT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 115, 126, STR_INTERCONTINENTAL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 153, 164, STR_HELISTATION, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 191, 202, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 191, 202, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA},
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 14, 27, STR_SMALL_AIRPORTS, STR_NULL},
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 52, 65, STR_LARGE_AIRPORTS, STR_NULL},
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 90, 103, STR_HUB_AIRPORTS, STR_NULL},
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 128, 141, STR_HELIPORTS, STR_NULL},
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 178, 191, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3001_AIRPORT_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 130, 0x0, STR_NULL},
{WWT_NODISTXTBTN, RESIZE_NONE, 14, 2, 73, 27, 38, STR_3059_SMALL, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{WWT_NODISTXTBTN, RESIZE_NONE, 14, 74, 145, 27, 38, STR_305A_LARGE, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{WWT_NODISTXTBTN, RESIZE_NONE, 14, 2, 145, 63, 74, STR_306B_HELIPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{WWT_NODISTXTBTN, RESIZE_NONE, 14, 2, 145, 39, 50, STR_305AA_LARGE, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{WWT_NODISTXTBTN, RESIZE_NONE, 14, 2, 145, 51, 62, STR_305AB_LARGE, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 88, 98, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 88, 98, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA},
{ WIDGETS_END},
};
static const WindowDesc _build_airport_desc = {
WDP_AUTO, WDP_AUTO, 148, 240,
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
-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
@@ -283,4 +247,5 @@ static void ShowBuildAirportPicker(void)
void InitializeAirportGui(void)
{
_selected_airport_type = AT_SMALL;
_last_built_aircraft_depot_tile = 0;
}

File diff suppressed because it is too large Load Diff

114
aystar.c
View File

@@ -19,30 +19,26 @@
#include "stdafx.h"
#include "openttd.h"
#include "aystar.h"
int _aystar_stats_open_size;
int _aystar_stats_closed_size;
// This looks in the Hash if a node exists in ClosedList
// If so, it returns the PathNode, else NULL
static PathNode* AyStarMain_ClosedList_IsInList(AyStar *aystar, const AyStarNode *node)
static PathNode *AyStarMain_ClosedList_IsInList(AyStar *aystar, AyStarNode *node)
{
return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction);
}
// This adds a node to the ClosedList
// It makes a copy of the data
static void AyStarMain_ClosedList_Add(AyStar *aystar, const PathNode *node)
static void AyStarMain_ClosedList_Add(AyStar *aystar, PathNode *node)
{
// Add a node to the ClosedList
PathNode *new_node = malloc(sizeof(*new_node));
PathNode *new_node = malloc(sizeof(PathNode));
*new_node = *node;
Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node);
}
// Checks if a node is in the OpenList
// If so, it returns the OpenListNode, else NULL
static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, const AyStarNode *node)
static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, AyStarNode *node)
{
return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction);
}
@@ -53,20 +49,19 @@ static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, const AyStarNo
static OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar)
{
// Return the item the Queue returns.. the best next OpenList item.
OpenListNode *res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
if (res != NULL) {
OpenListNode* res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
if (res != NULL)
Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction);
}
return res;
}
// Adds a node to the OpenList
// It makes a copy of node, and puts the pointer of parent in the struct
static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, const AyStarNode *node, int f, int g)
static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, AyStarNode *node, int f, int g)
{
// Add a new Node to the OpenList
OpenListNode *new_node = malloc(sizeof(*new_node));
OpenListNode* new_node = malloc(sizeof(OpenListNode));
new_node->g = g;
new_node->path.parent = parent;
new_node->path.node = *node;
@@ -79,10 +74,9 @@ static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, const AySt
/*
* Checks one tile and calculate his f-value
* return values:
* AYSTAR_DONE : indicates we are done
* AYSTAR_DONE : indicates we are done
*/
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
{
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
int new_f, new_g, new_h;
PathNode *closedlist_parent;
OpenListNode *check;
@@ -113,8 +107,7 @@ int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *pare
closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node);
// Check if this item is already in the OpenList
check = AyStarMain_OpenList_IsInList(aystar, current);
if (check != NULL) {
if ((check = AyStarMain_OpenList_IsInList(aystar, current)) != NULL) {
uint i;
// Yes, check if this g value is lower..
if (new_g > check->g) return AYSTAR_DONE;
@@ -123,9 +116,8 @@ int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *pare
check->g = new_g;
check->path.parent = closedlist_parent;
/* Copy user data, will probably have changed */
for (i = 0; i < lengthof(current->user_data); i++) {
for (i=0;i<lengthof(current->user_data);i++)
check->path.node.user_data[i] = current->user_data[i];
}
// Readd him in the OpenListQueue
aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f);
} else {
@@ -140,15 +132,14 @@ int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *pare
* This function is the core of AyStar. It handles one item and checks
* his neighbour items. If they are valid, they are added to be checked too.
* return values:
* AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
* has been found.
* AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
* reached.
* AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
* AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
* AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
* has been found.
* AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
* reached.
* AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
* AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
*/
int AyStarMain_Loop(AyStar *aystar)
{
int AyStarMain_Loop(AyStar *aystar) {
int i, r;
// Get the best node from OpenList
@@ -171,7 +162,7 @@ int AyStarMain_Loop(AyStar *aystar)
aystar->GetNeighbours(aystar, current);
// Go through all neighbours
for (i = 0; i < aystar->num_neighbours; i++) {
for (i=0;i<aystar->num_neighbours;i++) {
// Check and add them to the OpenList if needed
r = aystar->checktile(aystar, &aystar->neighbours[i], current);
}
@@ -179,20 +170,18 @@ int AyStarMain_Loop(AyStar *aystar)
// Free the node
free(current);
if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes) {
if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes)
/* We've expanded enough nodes */
return AYSTAR_LIMIT_REACHED;
} else {
else
// Return that we are still busy
return AYSTAR_STILL_BUSY;
}
}
/*
* This function frees the memory it allocated
*/
void AyStarMain_Free(AyStar *aystar)
{
void AyStarMain_Free(AyStar *aystar) {
aystar->OpenListQueue.free(&aystar->OpenListQueue, false);
/* 2nd argument above is false, below is true, to free the values only
* once */
@@ -207,8 +196,7 @@ void AyStarMain_Free(AyStar *aystar)
* This function make the memory go back to zero
* This function should be called when you are using the same instance again.
*/
void AyStarMain_Clear(AyStar *aystar)
{
void AyStarMain_Clear(AyStar *aystar) {
// Clean the Queue, but not the elements within. That will be done by
// the hash.
aystar->OpenListQueue.clear(&aystar->OpenListQueue, false);
@@ -224,9 +212,9 @@ void AyStarMain_Clear(AyStar *aystar)
/*
* This is the function you call to run AyStar.
* return values:
* AYSTAR_FOUND_END_NODE : indicates we found an end node.
* AYSTAR_NO_PATH : indicates that there was no path found.
* AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
* AYSTAR_FOUND_END_NODE : indicates we found an end node.
* AYSTAR_NO_PATH : indicates that there was no path found.
* AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
* When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY)
* aystar->clear() is called. Note that when you stop the algorithm halfway,
* you should still call clear() yourself!
@@ -237,26 +225,24 @@ int AyStarMain_Main(AyStar *aystar) {
// Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick
while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { }
#ifdef AYSTAR_DEBUG
switch (r) {
case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break;
case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break;
case AYSTAR_LIMIT_REACHED: printf("[AyStar] Exceeded search_nodes, no path found\n"); break;
default: break;
}
if (r == AYSTAR_FOUND_END_NODE)
printf("[AyStar] Found path!\n");
else if (r == AYSTAR_EMPTY_OPENLIST)
printf("[AyStar] OpenList run dry, no path found\n");
else if (r == AYSTAR_LIMIT_REACHED)
printf("[AyStar] Exceeded search_nodes, no path found\n");
#endif
if (r != AYSTAR_STILL_BUSY) {
if (r != AYSTAR_STILL_BUSY)
/* We're done, clean up */
_aystar_stats_open_size = aystar->OpenListHash.size;
_aystar_stats_closed_size = aystar->ClosedListHash.size;
aystar->clear(aystar);
}
switch (r) {
case AYSTAR_FOUND_END_NODE: return AYSTAR_FOUND_END_NODE;
case AYSTAR_EMPTY_OPENLIST:
case AYSTAR_LIMIT_REACHED: return AYSTAR_NO_PATH;
default: return AYSTAR_STILL_BUSY;
}
// Check result-value
if (r == AYSTAR_FOUND_END_NODE) return AYSTAR_FOUND_END_NODE;
// Check if we have some left in the OpenList
if (r == AYSTAR_EMPTY_OPENLIST || r == AYSTAR_LIMIT_REACHED) return AYSTAR_NO_PATH;
// Return we are still busy
return AYSTAR_STILL_BUSY;
}
/*
@@ -266,8 +252,7 @@ int AyStarMain_Main(AyStar *aystar) {
* clear() automatically when the algorithm finishes
* g is the cost for starting with this node.
*/
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g)
{
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g) {
#ifdef AYSTAR_DEBUG
printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n",
TileX(start_node->tile), TileY(start_node->tile), start_node->direction);
@@ -275,8 +260,7 @@ void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g)
AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, g);
}
void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets)
{
void init_AyStar(AyStar* aystar, Hash_HashProc hash, uint num_buckets) {
// Allocated the Hash for the OpenList and ClosedList
init_Hash(&aystar->OpenListHash, hash, num_buckets);
init_Hash(&aystar->ClosedListHash, hash, num_buckets);
@@ -287,10 +271,10 @@ void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets)
// That is why it can stay this high
init_BinaryHeap(&aystar->OpenListQueue, 102400);
aystar->addstart = AyStarMain_AddStartNode;
aystar->main = AyStarMain_Main;
aystar->loop = AyStarMain_Loop;
aystar->free = AyStarMain_Free;
aystar->clear = AyStarMain_Clear;
aystar->checktile = AyStarMain_CheckTile;
aystar->addstart = AyStarMain_AddStartNode;
aystar->main = AyStarMain_Main;
aystar->loop = AyStarMain_Loop;
aystar->free = AyStarMain_Free;
aystar->clear = AyStarMain_Clear;
aystar->checktile = AyStarMain_CheckTile;
}

View File

@@ -5,7 +5,7 @@
* AyStar is a fast pathfinding routine and is used for things like
* AI_pathfinding and Train_pathfinding.
* For more information about AyStar (A* Algorithm), you can look at
* http://en.wikipedia.org/wiki/A-star_search_algorithm
* http://en.wikipedia.org/wiki/A-star_search_algorithm
*/
#ifndef AYSTAR_H
@@ -55,8 +55,8 @@ typedef struct AyStar AyStar;
/*
* This function is called to check if the end-tile is found
* return values can be:
* AYSTAR_FOUND_END_NODE : indicates this is the end tile
* AYSTAR_DONE : indicates this is not the end tile (or direction was wrong)
* AYSTAR_FOUND_END_NODE : indicates this is the end tile
* AYSTAR_DONE : indicates this is not the end tile (or direction was wrong)
*/
/*
* The 2nd parameter should be OpenListNode, and NOT AyStarNode. AyStarNode is
@@ -71,8 +71,8 @@ typedef int32 AyStar_EndNodeCheck(AyStar *aystar, OpenListNode *current);
/*
* This function is called to calculate the G-value for AyStar Algorithm.
* return values can be:
* AYSTAR_INVALID_NODE : indicates an item is not valid (e.g.: unwalkable)
* Any value >= 0 : the g-value for this tile
* AYSTAR_INVALID_NODE : indicates an item is not valid (e.g.: unwalkable)
* Any value >= 0 : the g-value for this tile
*/
typedef int32 AyStar_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
@@ -81,7 +81,7 @@ typedef int32 AyStar_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNod
* Mostly, this must result the distance (Manhattan way) between the
* current point and the end point
* return values can be:
* Any value >= 0 : the h-value for this tile
* Any value >= 0 : the h-value for this tile
*/
typedef int32 AyStar_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
@@ -99,7 +99,7 @@ typedef void AyStar_GetNeighbours(AyStar *aystar, OpenListNode *current);
typedef void AyStar_FoundEndNode(AyStar *aystar, OpenListNode *current);
// For internal use, see aystar.c
typedef void AyStar_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g);
typedef void AyStar_AddStartNode(AyStar *aystar, AyStarNode* start_node, uint g);
typedef int AyStar_Main(AyStar *aystar);
typedef int AyStar_Loop(AyStar *aystar);
typedef int AyStar_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
@@ -112,11 +112,11 @@ struct AyStar {
/* These should point to the application specific routines that do the
* actual work */
AyStar_CalculateG *CalculateG;
AyStar_CalculateH *CalculateH;
AyStar_GetNeighbours *GetNeighbours;
AyStar_EndNodeCheck *EndNodeCheck;
AyStar_FoundEndNode *FoundEndNode;
AyStar_CalculateG* CalculateG;
AyStar_CalculateH* CalculateH;
AyStar_GetNeighbours* GetNeighbours;
AyStar_EndNodeCheck* EndNodeCheck;
AyStar_FoundEndNode* FoundEndNode;
/* These are completely untouched by AyStar, they can be accesed by
* the application specific routines to input and output data.
@@ -144,12 +144,12 @@ struct AyStar {
/* These will contain the methods for manipulating the AyStar. Only
* main() should be called externally */
AyStar_AddStartNode *addstart;
AyStar_Main *main;
AyStar_Loop *loop;
AyStar_Free *free;
AyStar_Clear *clear;
AyStar_CheckTile *checktile;
AyStar_AddStartNode* addstart;
AyStar_Main* main;
AyStar_Loop* loop;
AyStar_Free* free;
AyStar_Clear* clear;
AyStar_CheckTile* checktile;
/* These will contain the open and closed lists */
@@ -173,7 +173,7 @@ void AyStarMain_Clear(AyStar *aystar);
/* Initialize an AyStar. You should fill all appropriate fields before
* callling init_AyStar (see the declaration of AyStar for which fields are
* internal */
void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets);
void init_AyStar(AyStar* aystar, Hash_HashProc hash, uint num_buckets);
#endif /* AYSTAR_H */

378
bmp.c
View File

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

36
bmp.h
View File

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

View File

@@ -5,14 +5,10 @@
#ifndef BRIDGE_H
#define BRIDGE_H
enum {
MAX_BRIDGES = 13
};
/** Struct containing information about a single bridge type
*/
typedef struct Bridge {
Year avail_year; ///< the year in which the bridge becomes available
byte avail_year; ///< the year in which the bridge becomes available
byte min_length; ///< the minimum length of the bridge (not counting start and end tile)
byte max_length; ///< the maximum length of the bridge (not counting start and end tile)
uint16 price; ///< the relative price of the bridge
@@ -26,7 +22,4 @@ typedef struct Bridge {
extern const Bridge orig_bridge[MAX_BRIDGES];
extern Bridge _bridge[MAX_BRIDGES];
uint GetBridgeFoundation(Slope tileh, Axis axis);
uint GetBridgeHeightRamp(TileIndex t);
#endif /* BRIDGE_H */

View File

@@ -40,7 +40,7 @@ static void BuildBridge(Window *w, int i)
static void BuildBridgeWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
switch(e->event) {
case WE_PAINT: {
uint i;
@@ -50,7 +50,7 @@ static void BuildBridgeWndProc(Window *w, WindowEvent *e)
const Bridge *b = &_bridge[_bridgedata.indexes[i + w->vscroll.pos]];
SetDParam(2, _bridgedata.costs[i + w->vscroll.pos]);
SetDParam(1, b->speed);
SetDParam(1, (b->speed >> 4) * 10);
SetDParam(0, b->material);
DrawSprite(b->sprite, 3, 15 + i * 22);
@@ -59,9 +59,9 @@ static void BuildBridgeWndProc(Window *w, WindowEvent *e)
} break;
case WE_KEYPRESS: {
uint i = e->we.keypress.keycode - '1';
uint i = e->keypress.keycode - '1';
if (i < 9 && i < _bridgedata.count) {
e->we.keypress.cont = false;
e->keypress.cont = false;
BuildBridge(w, i);
}
@@ -69,8 +69,8 @@ static void BuildBridgeWndProc(Window *w, WindowEvent *e)
}
case WE_CLICK:
if (e->we.click.widget == 2) {
uint ind = ((int)e->we.click.pt.y - 14) / 22;
if (e->click.widget == 2) {
uint ind = ((int)e->click.pt.y - 14) / 22;
if (ind < 4 && (ind += w->vscroll.pos) < _bridgedata.count)
BuildBridge(w, ind);
}
@@ -79,16 +79,16 @@ static void BuildBridgeWndProc(Window *w, WindowEvent *e)
}
static const Widget _build_bridge_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_100D_SELECT_RAIL_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK},
{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_100D_SELECT_RAIL_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK},
{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WIDGETS_END},
};
static const WindowDesc _build_bridge_desc = {
WDP_AUTO, WDP_AUTO, 200, 102,
WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR,
-1, -1, 200, 102,
WC_BUILD_BRIDGE,WC_BUILD_TOOLBAR,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
_build_bridge_widgets,
BuildBridgeWndProc
@@ -96,16 +96,16 @@ static const WindowDesc _build_bridge_desc = {
static const Widget _build_road_bridge_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_1803_SELECT_ROAD_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK},
{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_1803_SELECT_ROAD_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK},
{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WIDGETS_END},
};
static const WindowDesc _build_road_bridge_desc = {
WDP_AUTO, WDP_AUTO, 200, 102,
WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR,
-1, -1, 200, 102,
WC_BUILD_BRIDGE,WC_BUILD_TOOLBAR,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
_build_road_bridge_widgets,
BuildBridgeWndProc
@@ -128,14 +128,14 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, byte bridge_type)
// only query bridge building possibility once, result is the same for all bridges!
// returns CMD_ERROR on failure, and price on success
ret = DoCommand(end, start, (bridge_type << 8), DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE);
ret = DoCommandByTile(end, start, (bridge_type << 8), DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE);
if (CmdFailed(ret)) {
errmsg = _error_message;
} else {
// check which bridges can be built
int bridge_len; // length of the middle parts of the bridge
int tot_bridgedata_len; // total length of bridge
int bridge_len; // length of the middle parts of the bridge
int tot_bridgedata_len; // total length of bridge
// get absolute bridge length
bridge_len = GetBridgeLength(start, end);
@@ -143,7 +143,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, byte bridge_type)
tot_bridgedata_len = CalcBridgeLenCostFactor(tot_bridgedata_len);
for (bridge_type = 0; bridge_type != MAX_BRIDGES; bridge_type++) { // loop for all bridgetypes
for (bridge_type = 0; bridge_type != MAX_BRIDGES; bridge_type++) { // loop for all bridgetypes
if (CheckBridge_Stuff(bridge_type, bridge_len)) {
const Bridge *b = &_bridge[bridge_type];
// bridge is accepted, add to list
@@ -162,6 +162,6 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, byte bridge_type)
w->vscroll.cap = 4;
w->vscroll.count = (byte)j;
} else {
ShowErrorMessage(errmsg, STR_5015_CAN_T_BUILD_BRIDGE_HERE, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE);
ShowErrorMessage(errmsg, STR_5015_CAN_T_BUILD_BRIDGE_HERE, TileX(end) * 16, TileY(end) * 16);
}
}

View File

@@ -1,37 +0,0 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "bridge_map.h"
TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir)
{
TileIndexDiff delta = TileOffsByDiagDir(dir);
assert(DiagDirToAxis(dir) == GetBridgeAxis(tile));
do {
tile += delta;
} while (!IsBridgeRamp(tile));
return tile;
}
TileIndex GetSouthernBridgeEnd(TileIndex t)
{
return GetBridgeEnd(t, AxisToDiagDir(GetBridgeAxis(t)));
}
TileIndex GetOtherBridgeEnd(TileIndex tile)
{
TileIndexDiff delta = TileOffsByDiagDir(GetBridgeRampDirection(tile));
do {
tile += delta;
} while (!IsBridgeRamp(tile));
return tile;
}

View File

@@ -7,7 +7,6 @@
#include "macros.h"
#include "map.h"
#include "rail.h"
#include "road_map.h"
#include "tile.h"
@@ -36,37 +35,16 @@ static inline bool IsBridgeMiddle(TileIndex t)
}
/**
* Determines which piece of a bridge is contained in the current tile
* @param tile The tile to analyze
* @return the piece
*/
static inline uint GetBridgePiece(TileIndex t)
{
assert(IsBridgeMiddle(t));
return GB(_m[t].m2, 0, 4);
}
/**
* Determines the type of bridge on a tile
* @param tile The tile to analyze
* @return The bridge type
*/
static inline uint GetBridgeType(TileIndex t)
{
assert(IsBridgeTile(t));
return GB(_m[t].m2, 4, 4);
}
/**
* Get the direction pointing onto the bridge
*/
static inline DiagDirection GetBridgeRampDirection(TileIndex t)
{
assert(IsBridgeRamp(t));
return ReverseDiagDir(XYNSToDiagDir((Axis)GB(_m[t].m5, 0, 1), GB(_m[t].m5, 5, 1)));
/* Heavy wizardry to convert the X/Y (bit 0) + N/S (bit 5) encoding of
* bridges to a DiagDirection
*/
return (DiagDirection)((6 - (_m[t].m5 >> 4 & 2) - (_m[t].m5 & 1)) % 4);
}
@@ -77,159 +55,10 @@ static inline Axis GetBridgeAxis(TileIndex t)
}
static inline TransportType GetBridgeTransportType(TileIndex t)
{
assert(IsBridgeTile(t));
return (TransportType)GB(_m[t].m5, 1, 2);
}
static inline bool IsClearUnderBridge(TileIndex t)
{
assert(IsBridgeMiddle(t));
return GB(_m[t].m5, 3, 3) == 0;
}
static inline bool IsWaterUnderBridge(TileIndex t)
{
assert(IsBridgeMiddle(t));
return GB(_m[t].m5, 3, 3) == 1;
}
static inline bool IsTransportUnderBridge(TileIndex t)
{
assert(IsBridgeMiddle(t));
return HASBIT(_m[t].m5, 5);
}
static inline TransportType GetTransportTypeUnderBridge(TileIndex t)
{
assert(IsTransportUnderBridge(t));
return (TransportType)GB(_m[t].m5, 3, 2);
}
static inline RoadBits GetRoadBitsUnderBridge(TileIndex t)
{
assert(GetTransportTypeUnderBridge(t) == TRANSPORT_ROAD);
return GetBridgeAxis(t) == AXIS_X ? ROAD_Y : ROAD_X;
}
static inline Track GetRailUnderBridge(TileIndex t)
{
assert(GetTransportTypeUnderBridge(t) == TRANSPORT_RAIL);
return AxisToTrack(OtherAxis(GetBridgeAxis(t)));
}
static inline TrackBits GetRailBitsUnderBridge(TileIndex t)
{
return TrackToTrackBits(GetRailUnderBridge(t));
}
/**
* Finds the end of a bridge in the specified direction starting at a middle tile
*/
TileIndex GetBridgeEnd(TileIndex, DiagDirection);
/**
* Finds the southern end of a bridge starting at a middle tile
*/
TileIndex GetSouthernBridgeEnd(TileIndex t);
/**
* Starting at one bridge end finds the other bridge end
*/
TileIndex GetOtherBridgeEnd(TileIndex);
uint GetBridgeHeight(TileIndex t);
static inline void SetClearUnderBridge(TileIndex t)
{
assert(IsBridgeMiddle(t));
SetTileOwner(t, OWNER_NONE);
SB(_m[t].m5, 3, 3, 0 << 2 | 0);
SB(_m[t].m3, 0, 4, 0);
}
static inline void SetWaterUnderBridge(TileIndex t)
{
assert(IsBridgeMiddle(t));
SetTileOwner(t, OWNER_WATER);
SB(_m[t].m5, 3, 3, 0 << 2 | 1);
SB(_m[t].m3, 0, 4, 0);
}
static inline void SetCanalUnderBridge(TileIndex t, Owner o)
{
assert(IsBridgeMiddle(t));
SetTileOwner(t, o);
SB(_m[t].m5, 3, 3, 0 << 2 | 1);
SB(_m[t].m3, 0, 4, 0);
}
static inline void SetRailUnderBridge(TileIndex t, Owner o, RailType r)
{
assert(IsBridgeMiddle(t));
SetTileOwner(t, o);
SB(_m[t].m5, 3, 3, 1 << 2 | TRANSPORT_RAIL);
SB(_m[t].m3, 0, 4, r);
}
static inline void SetRoadUnderBridge(TileIndex t, Owner o)
{
assert(IsBridgeMiddle(t));
SetTileOwner(t, o);
SB(_m[t].m5, 3, 3, 1 << 2 | TRANSPORT_ROAD);
SB(_m[t].m3, 0, 4, 0);
}
static inline void MakeBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d, TransportType tt)
{
uint northsouth = (d == DIAGDIR_NE || d == DIAGDIR_NW);
SetTileType(t, MP_TUNNELBRIDGE);
SetTileOwner(t, o);
_m[t].m2 = bridgetype << 4;
_m[t].m4 = 0;
_m[t].m5 = 1 << 7 | 0 << 6 | northsouth << 5 | tt << 1 | DiagDirToAxis(d);
}
static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d)
{
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD);
_m[t].m3 = 0;
}
static inline void MakeRailBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d, RailType r)
{
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL);
_m[t].m3 = r;
}
static inline void MakeBridgeMiddle(TileIndex t, uint bridgetype, uint piece, Axis a, TransportType tt)
{
SetTileType(t, MP_TUNNELBRIDGE);
SetTileOwner(t, OWNER_NONE);
_m[t].m2 = bridgetype << 4 | piece;
_m[t].m3 = 0;
_m[t].m4 = 0;
_m[t].m5 = 1 << 7 | 1 << 6 | 0 << 5 | 0 << 3 | tt << 1 | a;
}
static inline void MakeRoadBridgeMiddle(TileIndex t, uint bridgetype, uint piece, Axis a)
{
MakeBridgeMiddle(t, bridgetype, piece, a, TRANSPORT_ROAD);
}
static inline void MakeRailBridgeMiddle(TileIndex t, uint bridgetype, uint piece, Axis a, RailType r)
{
MakeBridgeMiddle(t, bridgetype, piece, a, TRANSPORT_RAIL);
SB(_m[t].m3, 4, 4, r);
}
#endif /* BRIDGE_MAP_H */
#endif

View File

@@ -1,492 +0,0 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "aircraft.h"
#include "debug.h"
#include "functions.h"
#include "table/sprites.h"
#include "table/strings.h"
#include "window.h"
#include "gui.h"
#include "vehicle.h"
#include "gfx.h"
#include "station.h"
#include "command.h"
#include "engine.h"
#include "player.h"
#include "depot.h"
#include "airport.h"
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "date.h"
#include "strings.h"
enum BuildVehicleWidgets {
BUILD_VEHICLE_WIDGET_CLOSEBOX = 0,
BUILD_VEHICLE_WIDGET_CAPTION,
BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING,
BUILD_VEHICLE_WIDGET_SORT_TEXT,
BUILD_VEHICLE_WIDGET_SORT_DROPDOWN,
BUILD_VEHICLE_WIDGET_LIST,
BUILD_VEHICLE_WIDGET_SCROLLBAR,
BUILD_VEHICLE_WIDGET_PANEL,
BUILD_VEHICLE_WIDGET_BUILD,
BUILD_VEHICLE_WIDGET_RENAME,
BUILD_VEHICLE_WIDGET_RESIZE,
};
static const Widget _build_vehicle_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW },
{ WWT_CAPTION, RESIZE_NONE, 14, 11, 239, 0, 13, STR_A005_NEW_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS },
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP},
{ WWT_PANEL, RESIZE_NONE, 14, 81, 227, 14, 25, 0x0, STR_SORT_CRITERIA_TIP},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 228, 239, 14, 25, STR_0225, STR_SORT_CRITERIA_TIP},
{ WWT_MATRIX, RESIZE_BOTTOM, 14, 0, 227, 26, 121, 0x401, STR_A025_AIRCRAFT_SELECTION_LIST },
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 228, 239, 26, 121, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST },
{ WWT_PANEL, RESIZE_TB, 14, 0, 239, 122, 213, 0x0, STR_NULL },
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 114, 214, 225, STR_A006_BUILD_AIRCRAFT, STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT },
{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 115, 227, 214, 225, STR_A037_RENAME, STR_A038_RENAME_AIRCRAFT_TYPE },
{ WWT_RESIZEBOX, RESIZE_TB, 14, 228, 239, 214, 225, 0x0, STR_RESIZE_BUTTON },
{ WIDGETS_END},
};
static bool _internal_sort_order; // descending/ascending
static byte _last_sort_criteria = 0;
static bool _last_sort_order = false;
static int CDECL EngineNumberSorter(const void *a, const void *b)
{
const EngineID va = *(const EngineID*)a;
const EngineID vb = *(const EngineID*)b;
int r = va - vb;
return _internal_sort_order ? -r : r;
}
static int CDECL EngineIntroDateSorter(const void *a, const void *b)
{
const int va = GetEngine(*(const EngineID*)a)->intro_date;
const int vb = GetEngine(*(const EngineID*)b)->intro_date;
const int r = va - vb;
if (r == 0) {
/* Use EngineID to sort instead since we want consistent sorting */
return EngineNumberSorter(a, b);
}
return _internal_sort_order ? -r : r;
}
static int CDECL EngineNameSorter(const void *a, const void *b)
{
static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
static char last_name[2][64] = { "\0", "\0" };
const EngineID va = *(const EngineID*)a;
const EngineID vb = *(const EngineID*)b;
int r;
if (va != last_engine[0]) {
last_engine[0] = va;
GetString(last_name[0], GetCustomEngineName(va), lastof(last_name[0]));
}
if (vb != last_engine[1]) {
last_engine[1] = vb;
GetString(last_name[1], GetCustomEngineName(vb), lastof(last_name[1]));
}
r = strcmp(last_name[0], last_name[1]); // sort by name
if (r == 0) {
/* Use EngineID to sort instead since we want consistent sorting */
return EngineNumberSorter(a, b);
}
return _internal_sort_order ? -r : r;
}
static int CDECL EngineReliabilitySorter(const void *a, const void *b)
{
const int va = GetEngine(*(const EngineID*)a)->reliability;
const int vb = GetEngine(*(const EngineID*)b)->reliability;
const int r = va - vb;
if (r == 0) {
/* Use EngineID to sort instead since we want consistent sorting */
return EngineNumberSorter(a, b);
}
return _internal_sort_order ? -r : r;
}
/* Aircraft sorting functions */
static int CDECL AircraftEngineCostSorter(const void *a, const void *b)
{
const int va = AircraftVehInfo(*(const EngineID*)a)->base_cost;
const int vb = AircraftVehInfo(*(const EngineID*)b)->base_cost;
int r = va - vb;
return _internal_sort_order ? -r : r;
}
static int CDECL AircraftEngineSpeedSorter(const void *a, const void *b)
{
const int va = AircraftVehInfo(*(const EngineID*)a)->max_speed;
const int vb = AircraftVehInfo(*(const EngineID*)b)->max_speed;
const int r = va - vb;
if (r == 0) {
/* Use EngineID to sort instead since we want consistent sorting */
return EngineNumberSorter(a, b);
}
return _internal_sort_order ? -r : r;
}
static int CDECL AircraftEngineRunningCostSorter(const void *a, const void *b)
{
const int va = AircraftVehInfo(*(const EngineID*)a)->running_cost;
const int vb = AircraftVehInfo(*(const EngineID*)b)->running_cost;
const int r = va - vb;
if (r == 0) {
/* Use EngineID to sort instead since we want consistent sorting */
return EngineNumberSorter(a, b);
}
return _internal_sort_order ? -r : r;
}
static int CDECL AircraftEngineCargoSorter(const void *a, const void *b)
{
const int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity;
const int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity;
const int r = va - vb;
if (r == 0) {
/* Use EngineID to sort instead since we want consistent sorting */
return EngineNumberSorter(a, b);
}
return _internal_sort_order ? -r : r;
}
static EngList_SortTypeFunction * const _aircraft_sorter[] = {
&EngineNumberSorter,
&AircraftEngineCostSorter,
&AircraftEngineSpeedSorter,
&EngineIntroDateSorter,
&EngineNameSorter,
&AircraftEngineRunningCostSorter,
&EngineReliabilitySorter,
&AircraftEngineCargoSorter,
};
static const StringID _aircraft_sort_listing[] = {
STR_ENGINE_SORT_ENGINE_ID,
STR_ENGINE_SORT_COST,
STR_SORT_BY_MAX_SPEED,
STR_ENGINE_SORT_INTRO_DATE,
STR_SORT_BY_DROPDOWN_NAME,
STR_ENGINE_SORT_RUNNING_COST,
STR_SORT_BY_RELIABILITY,
STR_ENGINE_SORT_CARGO_CAPACITY,
INVALID_STRING_ID
};
/**
* Draw the purchase info details of an aircraft at a given location.
* @param x,y location where to draw the info
* @param engine_number the engine of which to draw the info of
*/
void DrawAircraftPurchaseInfo(int x, int y, uint w, EngineID engine_number)
{
const AircraftVehicleInfo *avi = AircraftVehInfo(engine_number);
const Engine *e = GetEngine(engine_number);
CargoID cargo;
YearMonthDay ymd;
ConvertDateToYMD(e->intro_date, &ymd);
/* Purchase cost - Max speed */
SetDParam(0, avi->base_cost * (_price.aircraft_base>>3)>>5);
SetDParam(1, avi->max_speed * 128 / 10);
DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0);
y += 10;
/* Cargo capacity */
cargo = FindFirstRefittableCargo(engine_number);
if (cargo == CT_INVALID || cargo == CT_PASSENGERS) {
SetDParam(0, avi->passenger_capacity);
SetDParam(1, avi->mail_capacity);
DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, 0);
} else {
/* Note, if the default capacity is selected by the refit capacity
* callback, then the capacity shown is likely to be incorrect. */
SetDParam(0, cargo);
SetDParam(1, AircraftDefaultCargoCapacity(cargo, engine_number));
SetDParam(2, STR_9842_REFITTABLE);
DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0);
}
y += 10;
/* Running cost */
SetDParam(0, avi->running_cost * _price.aircraft_running >> 8);
DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0);
y += 10;
/* Design date - Life length */
SetDParam(0, ymd.year);
SetDParam(1, e->lifelength);
DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0);
y += 10;
/* Reliability */
SetDParam(0, e->reliability * 100 >> 16);
DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, 0);
y += 10;
/* Additional text from NewGRF */
y += ShowAdditionalText(x, y, w, engine_number);
y += ShowRefitOptionsList(x, y, w, engine_number);
}
void DrawAircraftImage(const Vehicle *v, int x, int y, VehicleID selection)
{
PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
DrawSprite(GetAircraftImage(v, DIR_W) | pal, x + 25, y + 10);
if (v->subtype == 0) {
SpriteID rotor_sprite = GetCustomRotorSprite(v, true);
if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
DrawSprite(rotor_sprite, x + 25, y + 5);
}
if (v->index == selection) {
DrawFrameRect(x - 1, y - 1, x + 58, y + 21, 0xF, FR_BORDERONLY);
}
}
void CcBuildAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2)
{
if (success) {
const Vehicle *v = GetVehicle(_new_vehicle_id);
if (v->tile == _backup_orders_tile) {
_backup_orders_tile = 0;
RestoreVehicleOrders(v, _backup_orders_data);
}
ShowAircraftViewWindow(v);
}
}
static void GenerateBuildAircraftList(Window *w)
{
EngineID eid, sel_id;
buildvehicle_d *bv = &WP(w, buildvehicle_d);
EngList_RemoveAll(&bv->eng_list);
/* Make list of all available planes.
* Also check to see if the previously selected plane is still available,
* and if not, reset selection to INVALID_ENGINE. This could be the case
* when planes become obsolete and are removed */
sel_id = INVALID_ENGINE;
for (eid = AIRCRAFT_ENGINES_INDEX; eid < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; eid++) {
if (IsEngineBuildable(eid, VEH_Aircraft, _local_player)) {
const AircraftVehicleInfo *avi = AircraftVehInfo(eid);
switch (bv->filter.acc_planes) {
case HELICOPTERS_ONLY:
if (avi->subtype != 0) continue; // if not helicopter
break;
case AIRCRAFT_ONLY:
if (avi->subtype == 0) continue; // if helicopter
break;
case ALL: break;
}
EngList_Add(&bv->eng_list, eid);
if (eid == bv->sel_engine) sel_id = eid;
}
}
bv->sel_engine = sel_id;
}
static void GenerateBuildList(Window *w)
{
buildvehicle_d *bv = &WP(w, buildvehicle_d);
switch (bv->vehicle_type) {
case VEH_Aircraft:
GenerateBuildAircraftList(w);
_internal_sort_order = bv->descending_sort_order;
EngList_Sort(&bv->eng_list, _aircraft_sorter[bv->sort_criteria]);
break;
default: NOT_REACHED();
}
}
static void DrawBuildAircraftWindow(Window *w)
{
const buildvehicle_d *bv = &WP(w, buildvehicle_d);
SetWindowWidgetDisabledState(w, BUILD_VEHICLE_WIDGET_BUILD, w->window_number == 0);
SetVScrollCount(w, EngList_Count(&bv->eng_list));
DrawWindowWidgets(w);
{
int x = 2;
int y = 27;
EngineID selected_id = bv->sel_engine;
EngineID eid = w->vscroll.pos;
uint16 max = min(w->vscroll.pos + w->vscroll.cap, EngList_Count(&bv->eng_list));
for (; eid < max; eid++) {
const EngineID engine = bv->eng_list[eid];
DrawString(x + 62, y + 7, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10);
DrawAircraftEngine(x + 29, y + 10, engine, GetEnginePalette(engine, _local_player));
y += 24;
}
if (selected_id != INVALID_ENGINE) {
const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL];
DrawAircraftPurchaseInfo(x, wi->top + 1, wi->right - wi->left - 2, selected_id);
}
}
DrawString(85, 15, _aircraft_sort_listing[bv->sort_criteria], 0x10);
DoDrawString(bv->descending_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10);
}
static void BuildAircraftClickEvent(Window *w, WindowEvent *e)
{
buildvehicle_d *bv = &WP(w, buildvehicle_d);
switch (e->we.click.widget) {
case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
bv->descending_sort_order ^= true;
_last_sort_order = bv->descending_sort_order;
GenerateBuildList(w);
SetWindowDirty(w);
break;
case BUILD_VEHICLE_WIDGET_LIST: {
uint i = (e->we.click.pt.y - 26) / 24 + w->vscroll.pos;
uint num_items = EngList_Count(&bv->eng_list);
bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE;
SetWindowDirty(w);
break;
}
case BUILD_VEHICLE_WIDGET_SORT_TEXT: case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:/* Select sorting criteria dropdown menu */
ShowDropDownMenu(w, _aircraft_sort_listing, bv->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0);
return;
case BUILD_VEHICLE_WIDGET_BUILD: {
EngineID sel_eng = bv->sel_engine;
if (sel_eng != INVALID_ENGINE) {
DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT));
}
break;
}
case BUILD_VEHICLE_WIDGET_RENAME: {
EngineID sel_eng = bv->sel_engine;
if (sel_eng != INVALID_ENGINE) {
bv->rename_engine = sel_eng;
ShowQueryString(GetCustomEngineName(sel_eng), STR_A039_RENAME_AIRCRAFT_TYPE, 31, 160, w->window_class, w->window_number, CS_ALPHANUMERAL);
}
break;
}
}
}
static void NewAircraftWndProc(Window *w, WindowEvent *e)
{
buildvehicle_d *bv = &WP(w, buildvehicle_d);
switch (e->event) {
case WE_INVALIDATE_DATA:
GenerateBuildList(w);
break;
case WE_DESTROY:
EngList_Destroy(&bv->eng_list);
break;
case WE_PAINT:
DrawBuildAircraftWindow(w);
break;
case WE_CLICK:
BuildAircraftClickEvent(w, e);
break;
case WE_ON_EDIT_TEXT: {
if (e->we.edittext.str[0] != '\0') {
_cmd_text = e->we.edittext.str;
DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE));
}
break;
}
case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
if (bv->sort_criteria != e->we.dropdown.index) {
bv->sort_criteria = _last_sort_criteria = e->we.dropdown.index;
GenerateBuildList(w);
}
SetWindowDirty(w);
break;
case WE_RESIZE:
w->vscroll.cap += e->we.sizing.diff.y / 24;
w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
break;
}
}
static const WindowDesc _build_vehicle_desc = {
WDP_AUTO, WDP_AUTO, 240, 226,
WC_BUILD_VEHICLE,0,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
_build_vehicle_widgets,
NewAircraftWndProc
};
void ShowBuildVehicleWindow(TileIndex tile, byte type)
{
buildvehicle_d *bv;
Window *w;
DeleteWindowById(WC_BUILD_VEHICLE, tile);
w = AllocateWindowDescFront(&_build_vehicle_desc, tile);
if (w == NULL) return;
w->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player;
w->resize.step_height = GetVehicleListHeight(type);
w->vscroll.cap = 4;
w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
bv = &WP(w, buildvehicle_d);
EngList_Create(&bv->eng_list);
bv->sel_engine = INVALID_ENGINE;
bv->sort_criteria = _last_sort_criteria;
bv->descending_sort_order = _last_sort_order;
bv->vehicle_type = type;
switch (type) {
case VEH_Aircraft: {
byte acc_planes = (tile == 0) ? ALL : GetAirport(GetStationByTile(tile)->airport_type)->acc_planes;
bv->filter.acc_planes = acc_planes;
break;
}
default: NOT_REACHED();
}
GenerateBuildList(w);
/* Select the first plane in the list as default when opening the window */
if (EngList_Count(&bv->eng_list) > 0) bv->sel_engine = bv->eng_list[0];
}

View File

@@ -23,9 +23,6 @@ CommandCallback CcBuildBridge;
CommandCallback CcBuildDocks;
CommandCallback CcBuildCanal;
/* depot_gui.c */
CommandCallback CcCloneVehicle;
/* main_gui.c */
CommandCallback CcPlaySound10;
CommandCallback CcPlaceSign;
@@ -84,8 +81,7 @@ CommandCallback *_callback_table[] = {
/* 0x16 */ CcCloneRoadVeh,
/* 0x17 */ CcCloneShip,
/* 0x18 */ CcCloneTrain,
/* 0x19 */ CcAI,
/* 0x1A */ CcCloneVehicle
/* 0x19 */ CcAI
};
const int _callback_table_count = lengthof(_callback_table);

View File

@@ -3,7 +3,7 @@
#ifndef CALLBACK_TABLE_H
#define CALLBACK_TABLE_H
#include "command.h"
#include "functions.h"
extern CommandCallback *_callback_table[];
extern const int _callback_table_count;

View File

@@ -1,345 +1,9 @@
0.5.2 (2007-05-29)
------------------------------------------------------------------------
- Feature: Add threading support for MorphOS (r9759)
- Fix: Bridges and tunnels were not always removed on bankruptcy, thus leaving tunnels/bridges with an invalid owner that would crash the game when clicking with the query tool on them (r9966)
- Fix: Null pointer dereference under MorphOS and AmigaOS (r9861)
0.5.2-RC1 (2007-05-16)
------------------------------------------------------------------------
- Feature: Windows 95/98/ME check in Windows 2000/XP/2003/Vista builds (r9834)
- Feature: Add password protected status to 'players' (network server) console command (r9771)
- Feature: Add server_lang in [network] section of openttd.cfg (r9716)
- Fix: Loading some TTDP savegames caused an instant assertion on loading (r9857)
- Fix: [NewGRF] Catch occurance of division-by-zero in varaction handling (r9837)
- Fix: Only non dedicated servers cannot have 0 players [FS#765] (r9785)
- Fix: Remove arbitrary limit on length of NewGRF strings (r9775)
- Fix: [NewGRF] Ignore axis-bit of station tile layouts [FS#756] (r9758)
- Fix: [win32] Dead key and open/close console. (r9728)
- Fix: When you have closed the "Load game"/"New game" windows which you started from the "start server" menu, you shouldn't start a server when starting a new game [SF#1244842] (r9757)
- Fix: Trains were lost after autorenewal/autoreplace [FS#732] (r9753)
- Fix: Stop flooded towns from building roads on water [FS#598] (r9743)
- Fix: Station signs were not resized when the language changed [FS#672] (r9741)
- Fix: In news history, newlines were not replaced with spaces [FS#677] (r9731)
- Fix: Crash when destroying bridge with train partially on it [FS#738] (r9726)
- Fix: Planes made a 270 degree turn instead of a 90 degree turn on the southern runway of the intercontinental airport [FS#743] (r9725)
- Fix: In-game private messages did not work for clients with high ClientIDs (r9719)
- Fix: Do not allow building of rail vehicles whose railtype is not available (r9718)
- Fix: [YAPF] The guessed path was ignored for ships [FS#736] (r9694)
0.5.1 (2007-04-20)
------------------------------------------------------------------------
(None)
0.5.1-RC3 (2007-04-17)
------------------------------------------------------------------------
- Feature: Add list_patches to console commands; shows all patches and values (r9565)
- Fix: Select "Custom" in the difficulty settings gui when changing a setting [FS#733] (r9647)
- Fix: Building rail on steep slopes ignored build_on_slopes patch setting (r9602)
- Fix: Wrong characters in Finnish town names (r9641)
- Fix: When checking for no vehicle on ground-tiles, do not take into account vehicles that are in the air (r9542)
- Fix: Bankrupt AIs no longer buy over themselves (also added safeguards to prevent in future) (r9540 / r9541)
- Fix: When company is removed, sell all shares of the and in the company (r9533)
- Fix: Crash when 2 or more clients joined at roughly the same time (r9529)
- Fix: Custom currency was overwritten and fix euro introduction (r9467, r9469)
- Fix: Values of diff_custom and snow_line in .cfg were not checked properly (r9455)
- Fix: When deleting a vehicle which has shared orders with one more vehicle and no orders, segfaulted (r9429)
0.5.1-RC2 (2007-03-23)
------------------------------------------------------------------------
- Fix: crashes when the chatbox would be drawn outside of the main window [FS#701] (r9420)
- Fix: reading out of an array caused a segmentation fault [FS#694] (r9394)
0.5.1-RC1 (2007-03-20)
------------------------------------------------------------------------
- Feature: Translation dependant formatting of dates (r8906)
- Feature: Kick inactive initial network connections after some time [FS#115] (r9038, r9061)
- Feature: Add an extra news group for opening and closing of industries (r9097)
- Codechange: Disable shares by default and increase the default maximum distance from edge for oil refineries (r9339)
- Codechange: When you started openttd with '-g' you got the same map every run (r9205)
- Codechange: When all news-setting buttons are 'full', make the for-all button show 'full' too (r9137)
- Codechange: Disable the ability to make flooding water with the canal build tool. In the scenario editor you can still make both canals and flood water at height level 0 [FS#622, FS#629] (r9105, r9115)
- Codechange: The station list, sorted by cargo rating, now takes stations into account that have no cargo waiting [FS#595] (r9062)
- Fix: Close the Shared Order Vehicle List if you remove the shared link with only 2 vehicles (r9338)
- Fix: A34-1000, Z-Shuttle, and Kelling K1 are now listed as small aircraft (r9298)
- Fix: Shared orders got messed up when the 'first' trains got removed in the depot [FS#685] (r9277)
- Fix: Use a less CPU-intensive algorithm to find a random industry for the AI to prevent it slowing down the game [FS#644] (r9251)
- Fix: When loading games, enroute_from was updated in the wrong place, causing issues with TTD savegames/scenarios (r9147)
- Fix: "Train is lost" message is generated incorrectly [FS#676] (r9146)
- Fix: Difficulty level button was not selected when opening the difficulty window (r9117)
- Fix: The wrong catenary wires were drawn for tunnel entrances [FS#612] (r9077)
- Fix: The intercontinental airport used 'T-junction' runway sprites when there is no exit in the middle of the runway as in the city airport [FS#529] (r9076)
- Fix: [win] dedicated console now doesn't need an extra 'enter' to fully quit [FS#459] (r9074)
- Fix: Take over companies properly in multiplayer games [FS#459] (r9071)
- Fix: When a bribe failed and you haven't picked up cargo yet, you would never be able to do so for a given station [FS#404] (r9070)
- Fix: Don't keep on scrolling for non-numeric values in settings, but require reclick [FS#663] (r9064)
- Fix: The personal (.openttd) directories were hidden in the load/save directory listings [FS#652] (r9043)
- Fix: Desync caused by buffer overflow [FS#664] (r9027)
- Fix: When cutting strings into multiple lines also take into consideration whitespace characters of more than 1 byte length. (r9012)
- Fix: Play the correct engine sound based on the engine type instead of the sprite (r9009)
- Fix: New locomotive names were not announced in the news, it said "new railway locomotive available - railway locomotive" [FS#581] (r9000, r9001)
- Fix: [NewGRF] Do not select a disabled platform length/number of track count when going out of drag-drop mode [FS#450] (r8999)
- Fix: [win] Resolution doubled in cfg file when fullscreen mode used [FS#642] (r8994)
- Fix: The industry list should also be (re)set when the number of industries is 0 [FS#656] (r8980)
- Fix: [win] Possible buffer overflow if unicode text is pasted into an input box and needs trimming. (r8975)
- Fix: [win] Support compilation with the Vista Platform SDK (r8974)
- Fix: Crash on loading savegames with GRFs that do not have their GRF info/name set (r8955)
- Fix: [NewGRF] support for vehicle variable 48 was wrong (r8943)
0.5.0 (2007-02-27)
------------------------------------------------------------------------
- Feature: Add the ability to load newer TTDP games (the tile information for coasts has changed) (r8738)
- Feature: Selecting "end of orders" and deleting will delete all the vehicle's orders (shared mode unchanged) (r8685)
- Codechange: Call GetFirstVehicleInChain only for trains thus increasing performance in large games (r8744)
- Fix: Possible crashes, problems with aircraft and airport removal (r8921)
- Fix: Do not show the 'edit sign' window for spectators (r8808)
- Fix: Adhere order types for ship order insertion to determine destination type (r8802)
- Fix: It was possible to take over buoys by building a station next to them (r8794)
- Fix: Cloning unaware of articulated locomotives that could refit without refitting the front unit (r8777)
- Fix: Loading times for overhanging trains are miscomputed (r8709)
0.5.0-RC5 (2007-02-08)
------------------------------------------------------------------------
- Feature: Requery gameservers that did not respond to their first query (r8520, r8542)
- Feature: Logging of the IP address and port of invalid/illegal UDP packets (r8490)
- Codechange: Replace missing sprites with a red question mark (r8634)
- Codechange: Add Korean, Simplified Chinese and Traditional Chinese languages as an official translation (r8286, r8324, r8616)
- Codechange: Increase the size of the sound/video/music-drivers to 32 bytes (instead of 16) so their actual parameters can be passed. Sound has for example bufsize' and 'hz' (r8497)
- Codechange: Be more strict about language generation and fail any languages not having the mandatory ##name, ##ownname and ##isocode pragma's (r8253)
- Fix: Draw canal edges under buoys that are in a canal (r8635)
- Fix: Buoys on canal tiles do not flood anymore (r8620)
- Fix: Store the ownership of a water tile in the buoy tile and set the ownership of the water tile when the buoy is removed. Prevents certain abuses (r8619)
- Fix: When the currently selected player in the performance details window is no longer active, choose the first active player instead of the first player as that may also be inactive [FS#582] (r8612)
- Fix: Road vehicle very close after another (slower) road vehicle gets its speed reset to 0 when entering a tunnel, which causes a traffic jam outside of the tunnel (r8609)
- Fix: Bridges do not get destroyed when the bridge head gets flooded and there is a vehicle on the bridge [FS#564] (r8593)
- Fix: Road Vehicles now can obtain a slot even if the station is very spread out [FS#577] (r8536)
- Fix: Segmentation fault when the toolbar gets removed and you have selected one of the items in a submenu of the toolbar (r8533)
- Fix: Deleting a vehicle with shared orders, but no orders would fail to reset prev_shared and next_shared (r8294)
0.5.0-RC4 (2007-01-18)
------------------------------------------------------------------------
- Feature: Increase spritecache size to 2MB, will increase performance in games using newgrf files (r8218)
- Feature: OS/2 support with GCC (Watcom is dropped) (r8042)
- Codechange: Add Japanese, Slovenian language as an official translation and split Norwegian into Bokmal and Nynorsk (r7987, r8084, r8069)
- Codechange: Show error messages about our own data files as a popup, or to stderr if console is available (and not to stdout) (r8013, r8134)
- Codechange: Change the ordering of the network list, compatible servers just missing grf files are below fully compatible servers, not on the bottom (r8118)
- Fix: Return proper error value when unthreaded save fails, prevents server sending 0-sized files (r8171)
- Fix: Network client crashes when a server sends a 0-sized savegame [FS#556] (r8167)
- Fix: Several desync fixes (incorrect roadstop update of old games, autoreplace bugs) [FS#551] (r8137, r8147, r8157)
- Fix: Some disaster-events fixed: combat chopper shoots from right position, submarine once again moves around (r8140, r8158)
- Fix: "out of sprite memory" warning messages due to incorrect assumption of requested memory for sprites (r8133)
- Fix: Bouys are now built and numbered 1..9 not 9..1 [FS#538] (r8123)
- Fix: Clicking for more news properly cycles through the news history backwards, and doesn't show the first item doubly if it's already open (r8049)
- Fix: Crash when removing a town in the scenario editor while the query window is open for one of the town's tiles (r8030)
- Fix: Overflow of system-ticks was not handled properly, resulting in a possibly unresponsive server/client (r8028)
- Fix: Automatic pause interfering with 'pause_on_join setting' in MP when <shift> is pressed [FS#486] (r8027)
- Fix: Picking up en-route cargo will also have virtual profit deducted for trains aswell (r8026)
- Fix: Out-of-bounds read access on _clients array (harmless) (r7984)
0.5.0-RC3 (2007-01-07)
------------------------------------------------------------------------
- Codechange: Add Lithuanian language as an official translation (r7806)
- Fix: The configure script did not work work for dash, a sh compatible shell [FS#485] (r7893)
- Fix: [OSX] control + enter no longer fullscreens, interfered with team-chat (r7886)
- Fix: Offset engines/wagons by half width in details window; fixes overflowing for display (r7864)
- Fix: [OSX] Remove incorrect debug message about missing grf files. (r7766)
- Fix: [sdl] sometimes ALT-TAB could trigger the fast forward (r7727)
0.5.0-RC2 (2006-12-31)
------------------------------------------------------------------------
- General Removed support for OSX older than 10.3.9. Either upgrade, or use 0.4.8 (compatible with OSX 10.2)
- Codechange: Drastically reduce the CPU usage in certain cases (AI using CheckStationSpreadOut()) (r7585)
- Fix: Internal bug in updating the animated_tiles table caused desyncs between (different endian) machines in MP (r7631)
- Fix: Signal update got propagated through incompatible railtypes and under certain circumstances tunnels and rail on top (r7620)
- Fix: Remove landscaping toolbar option from road consturction toolbar in scenario editor [FS#473] (r7586)
- Fix: The server could under certain circumstances tell a client too late to start syncing if it has been waiting to join (r7566)
- Fix: Removing towns in scenario editor didn't remove their subsidies causing possible crashes [FS#468] (r7563)
- Fix: Internal and (patches) GUI were disagreeing about autorenew settings [FS#431] (r7561)
- Fix: No new company could be created if more than 8 clients were connected, even if not all 8 companies were used (r7560)
- Fix; Clicking 'full load' could under certain circumstances change the current depot order [FS#456] (r7559)
- Fix: Do not wait till a crashed vehicle is removed before starting to load other vehicles [FS#464] (r7558)
- Fix: MorhpOS compile and install fixes (r7548)
- Fix: Removing rail station cost was calculated on occupied area not on number of tiles with an actual station on (r7547)
0.5.0-RC1 (2006-12-21)
------------------------------------------------------------------------
- General fixes and improvements to TTDPatch's newgrf format, most noticable are newstations, newsounds, more callbacks and I18n
- Added languages: Bulgarian, Esperanto, Russian, Ukrainian, Languages with proper diacretics: Czech, Hungarian, Turkish
- Feature: Show NewGRF compatability of network games; green for full compatibility, yellow for missing newgrfs and red for invalid revision (r7505)
- Feature: Load a list of NewGRFs from the config (in the [newgrf-static] section) that should always be loaded (r7490)
- Feature: Double the length of the cargo and rating indicators in the station list window for better visibility (r7466)
- Feature: NewGRF set up window and browser which allows modification and viewing of NewGRF settings ingame or the main menu (r7357)
- Feature: Support for saving NewGRF settings with savegames (r7348)
- Feature: Add support for gradual (un)loading of vehicles (r7326)
- Feature: Add freight trains patch option which is a multiplier for the weight of cargo on freight trains, to simulate longer heavier trains (r7269)
- Feature: UNICODE/UTF8 support, with (optional) usage of fonts rendered by Freetype instead of sprites. This means full unicode support (input, rendering, file/io) and greatly enhanced internationalization for non-latin languages (utf8) (r7182)
- Feature: Add Slovak, Brazil and Slovenian currency [SF 1243657, 1171147; FS#131] (r7160, r5964)
- Feature: Allow towns to be built on top of trees in the scenario editor [FS#396] (r7152)
- Feature: Allow over-building of compatible railtypes, i.e. normal and electrified rail. If building electrified rail, normal rail is upgraded for you (at a cost) (r7106)
- Feature: Additional positioning for long dropdown lists with scrollbar support if dropdown list wouldn't fit (r7086)
- Feature: [win32] Remember the window size between restarts when quit in fullscreen mode (r7061)
- Feature: Increase the chatbuffer of chat messages, messages longer than the graphical box will be wrapped to a new line (r6956)
- Feature: Allow typing longer text than visible for an editbox; it will scroll properly now (r6954)
- Feature: Allow spectators to team-speak to eachother (r6933)
- Feature: Allow for " to be in console tokens. Escape them with \. eg \" (r6875)
- Feature: Change the functionality of the chat window. SHIFT+ENTER (SHIFT+T) sends a message to all players, CTRL+ENTER (CTRL+T) sends a message to all team mates and ENTER (T) is customizable (r6824)
- Feature: (Train is) lost message is now generated immediately when pathfinder can't find the path (r6800)
- Feature: Add a measurement tool that will show dimensions and height differences of various draggable tools (r6758)
- Feature: Added sort options to the build aircraft and train windows (r6708)
- Feature: Depot lists are now sorted, so vehicle 1 is always first and so on (r6652)
- Feature: Ability to pause a server if not enough players are connected. The setting for this is 'min_players' (r6628)
- Feature: Ability for servers to execute a script just after a client has connected, e.g. for a MOTD, etc (r6625)
- Feature: Add refit commands to vehicle orders (can only be done in goto depot orders) (r6624)
- Feature: Support cargo subtypes in the refit window. The refit window has been altered to support resizing and scrolling (r6601)
- Feature: Depot and vehicle list windows reworked a bit with more buttons to include 'Autoreplace all' (instantly), 'Sell all', 'Start all' and 'Stop all' (r6542, r6552, r6515)
- Feature: Using goto depot with a different control selection will now alter the service/stopping in depot flag instead of cancelling the goto depot order (r6295)
- Feature: When automatically detecting the language try to first match language+territory (e.g. de_CH), then just language (e.g. de) and fall back to en_GB otherwise (r6290)
- Feature: Add a "goto depot" button to various vehicle list windows (r6229, r6246)
- Feature: Save max_companies/clients/spectators in the config file (r6170)
- Feature: Vehicle status bar will show the heading string in different colours to visually discern the difference between a service and a forced stop (r6165, r6414)
- Feature: Control clicking Goto Depot will now make the vehicle service instead of stop in depot (r6165)
- Feature: List of vehicles with the same shared orders, accessible from the orders-window of a given vehicle (r6161)
- Feature: Added -s (source) and -d (destination) to strgen to specify paths for input and output files (r6089)
- Feature: After removing a farm, its farmland is removed too (over time) [FS#82]
- Feature: Clicking twice on the location button in the smallmap centers to your position, clicking twice centres your viewport [FS#54] (r6040)
- Feature: Change the original date format to a 32 bits format based at the year 0. Highest date is the year 5.000.000AC (r5999)
- Feature: Auto-completion in chat-window. It completes Player and Town names (in that order) using <tab> (r5968)
- Feature: Catalan Town Names generator [FS#261] (r5965)
- Feature: Possibility to generate scenarios by importing heightmaps. It can be in PNG or BMP format
- Feature: New (optional) landscape generator based on TerraGenesis Perlin noise with GUI, progress bar and fine-tuning options (r5946)
- Feature: Filter for textboxes to only allow input of certain patterns (like numbers only) (r5944)
- Feature: [win32] Remember the maximized state of the game window and restore on start [FS#234] (r5874)
- Feature: Add an icon to the SDL openttd executable (r5872)
- Feature: Also allow horizontal and vertical rails on steep slopes (r5864)
- Feature: Allow building of (certain) rails, roads and bridge ramps on steep sloped tiles (r5833)
- Feature: Replacing from a train engine without cargo capacity to one with cargo capacity will now make autoreplace refit the engine to carry the cargo type from the last wagon in the train (r5465)
- Feature: [OSX] Macs with touchpads that support two finger scrolling can now use this "scrollwheel" to scroll up/down (r5460)
- Feature: Allow building canals at sea-level, using ctrl to toggle canal or plain water tile. This allows building of non-raisable sea-level water ways (useful in multiplayer) and dikes for low-level areas (r5403)
- Feature: Add 4 new airports. 2 for aircraft, 2 for helicopters (r5346)
- Feature: Implement smooth horizontal depot and, vehicle list scrolling for trains (r5046)
- Feature: Add new pathfinder, YAPF. Has greatly improved performance and better, fully configurable, pathfinding (yapf) (r4987)
- Feature: Add a new console command "players" that lists current players along with basic stats [FS#150] (r4828)
- Feature: Station List View can now be sorted and filtered (by waiting cargo type and facilities) (r4822)
- Feature: The integer-list parser now accepts a space character as an item seperator next to the comma for openttd.cfg (r4490)
- Feature: Add support for electric railways as a seperate tracktype. Electric trains will not run on non-electrfied track unless otherwise controlled by patch option (elrails) (r4150)
- Feature: A new multi-lingual multi-measuring-unit system (r4126)
- Feature: Add proper OPENTTD <> LOCALCODE conversion using iconv. Savegames with special characters will be legible in filesystem (r4105)
- Feature: Undraw the mouse when it leaves the window and draw it again when it enters (r4075, r4083)
- Feature: It is now possible to turn a single unit in a train (CTRL+Click on unit in depot) (r3944)
- Feature: Delete news items about vehicles when they get stale (r3757)
- Feature: Save patch settings with the savegame so you are presented with the same behaviour when loading the game on another machine/installment (r3726)
- Feature: Add 2cc (two company colours) livery schemes. This replaces the original colour selection window (r3717, r6455)
- Feature: [OSX] Added support for tripple binaries (binaries optimised for G3, G5 and i686) (r3674)
- Feature: Allow autoreplacing of train wagons (r3535)
- Feature: Allow sorting of vehicle lists by model or value (r3528)
- Feature: Allow trains details view to be resized (r3521)
- Codechange: Improve the usability of the signal-dragger, do not bail out at (certain) errors, just silently ignore them [FS#149] (r7127)
- Codechange: Make the zoom in/out buttons of the extra viewport into proper push-buttons (r7078)
- Codechange: Make the AI choose road vehicles based on a rating (currently max speed * capacity) instead of either the cost or the index of the vehicle (r7070)
- Codechange: Truncate text in window captions to prevent overflow (r7058)
- Codechange: Allow standard ini-file style comments (;) (r6972)
- Codechange: Send server messages with format NETWORK_ACTION_SERVER_MESSAGE so it is general colour like the rest of the server messages. Spectators speak in grey (r6932)
- Codechange: Change textmessage format a bit. Only the sender's name and target are in the sender's colour, the actual message is in white. Should improve readability (r6932)
- Codechange: Add an MD5 sum check of our own data files, and warn if they don't match (r6921)
- Codechange: Add strict bounds checking in string formatting system to check for possible buffer overflows (r6884)
- Codechange: Have the dropdown menus fall fully inside the top toolbar (r6745)
- Codechange: Determine the length of the main toolbar dropdown list based on the length of the strings in that list (r6744)
- Codechange: When vehicles never expire they will stay at peak reliability instead of the lowest to make them useful even when old (r6681)
- Codechange: Show more correct capacity of articulated wagons in the train purchase list (r6650)
- Codechange: When showing tooltips, properly position the tooltip taking into account window dimensions and cursor (r6405)
- Codechange: Speed up the animated cursors a bit so they move once in a while at least (r6367)
- Codechange: Remove the "unsorted" vehicle sorter, because it's plain useless (r6270)
- Codechange: Remove MSVC6 support. The compiler was too stupid and too many workarounds were needed. Please switch to mingw or VC2005++ express (r5286)
- Codechange: Allow a switch in Makefile.config to disable threads in OpenTTD (r5978)
- Codechange: [win32] Add native x64 target to VS2005 project files (r5813)
- Codechange: [win32]The exception dialog showed the last modification-date of win32.c instead of the last compilation-date (r5801)
- Codechange: Add owner attribute to canals and locks. This makes them more useful in multiplayer games, as only the owner can delete them. Does not affect usage (r5084)
- Codechange: Add MSVC2005 support, project and solution files are in the _vs80.* files (r4581)
- Codechange: [OSX] Shark (Xcode's profiling tool) can now relate CPU usage to lines (r3611)
- Codechange: Rewrite the multistop slot assignment system. More resource-friendly, several slot-assignment improvements (r3730, r4259)
- Codechange: Completely remove the deprecated -p parameter (is superseded by -n) (r3508)
- Fix: Town ratings were not reset when a company went bankrupt (r7433)
- Fix: With realistic acceleration, guarantee a minimum braking force is applied. This ensures trains will stop when going down hill (r7425)
- Fix: Changed "kick off" acceleration resulted in only a small amount of power being applied; this resulted in a perceived delay before trains moved. (r7421)
- Fix: Long delay for message windows to appear. Immediately show a new message if present if no news window is open, or has just been closed instead of waiting for the timer of the current news to time out [FS#255] (r7402)
- Fix: Deleting Train in depot with autoreplace fails [FS#418] (r7385)
- Fix: Don't update vehicle images when turning a train around. During this procedure the train is split into parts which can result in incorrect images being used (r7378)
- Fix: OpenTTD could crash under certain circumstances when a vehicle as autoreplaced and a news window was open [FS#332] (r7368)
- Fix: Segmentation fault in the SDL video driver when one goes to fullscreen and there are no suitable resolutions (r7332)
- Fix: When loading a game from a dedicated server the local player set to 0, theoretically enabling the dedicated server to also play (r7312)
- Fix: TTDPatch vars are little endian (r7282)
- Fix: Always display the excavation of roadworks even when fully zoomed out or "full details" are off (r7240)
- Fix: Window allocation and deletion messed with the actual window pointer, possibly crashing OpenTTD [FS#350, SF#1560913] (r7205)
- Fix: Callback not executed for non-player based patch changes in multiplayer for all clients; possible desync issue (r7190)
- Fix: Station sign (and base station coordinates) didn't move along with station when station moved by walking. [FS#388] (r7169)
- Fix: MiniMap was misplacing vehicles sometimes [FS#402] (r7166)
- Fix: Some mouse events possibly lost under high CPU load, handle mouse input right away instead of waiting for GameLoop. [FS#221, SF1168820] (r7157)
- Fix: Some keyboard events possibly lost under high CPU load, handle keyboard input in place instead of global variables magic. [FS#279] (r7153)
- Fix: "Position of Main Toolbar" option isn't honored when starting new game or loading saved [FS#172] (r7130)
- Fix: Synchronize the engine-renew settings of a player when joining a multiplayer game (r7126)
- Fix: Several errors/glitches related to multiplayer and bankrupcy (mainly server), and non-updated company-information (r7125)
- Fix: Cloning a vehicle that has been refitted would incur the expense as running costs, not new vehicles [FS#371] (r7115)
- Fix: Do not let ships enter partial water tiles under bridges; they will travel up land... (r7110)
- Fix: AI tried to build road from the back or side of road stop/depot (r7069)
- Fix: Zooming out near map-borders would previously fail because the new centre would be outside the map [FS#317] (r7047)
- Fix: 'Goto' button in orders window got depressed along with all other buttons when an existing order was modified [FS#311] (r7046)
- Fix: Scenario bridges/tunnels cannot be demolished [FS#200] (r7028)
- Fix: Pressing F1 in scenario editor did not work (r7023)
- Fix: Properly guard against viewing company-sensitive information from invalid players (eg spectators) which could lead to crashes [FS#292] (r7022)
- Fix: In the replace vehicle window, the left vehicle list was not drawn when an engine was not selected (r7009)
- Fix: Crash at game end when server company is bankrupt [FS#369] (r7008)
- Fix: List of actions panel in the town authority window went underneath its scrollbar (r6885)
- Fix: Pressing ^D (EOF) at a dedicated console caused it to repeat the last command, instead of doing nothing (r6835)
- Fix: Don't add up running cost of articulated engine parts (r6765)
- Fix: If a rail is not available, don't show toolbar even with hotkey 'A' (r6740)
- Fix: Only apply the virtual transfer profit if the order is a transfer order, rather than to any unload order (r6738)
- Fix: Disable main toolbar buttons showing company list drop downs when there are no companies [FS#356] (r6695)
- Fix: Autoreplace can now use the money for selling the old vehicle to build the new one (r6640)
- Fix: A loop-hole that allowed docks to be built regardless of town authority rating (r6477)
- Fix: [win32] The dedicated server could overwrite the keyboard input buffer before it was handled by OpenTTD (r6449)
- Fix: Reset the location of the last sound as that location can be outside the map when you are loading another, (smaller) map (r6437)
- Fix: Show an error message when executing 'scrollto x' with x < 0 or >= MapSize() instead of asserting later on [FS#340] (r6435)
- Fix: Station catchment area persists after switching tools [FS#136] (r6368)
- Fix: Do not reset the current cursor action when centering on a depot/hangar (r6360)
- Fix: Go to hangar orders for aircraft could get spuriously removed when a road or rail depot got deleted (r6355)
- Fix: Due to some off-by-one errors the width or height of a clipping rectangle could become 0, which isn't sensible. This should fix a very rare and hard to trigger assertion in GfxFillRect() (r6351)
- Fix: Never allow scrolling the map in the main menu (scroll-settings weren't reset if switched to mainmenu) (r6037)
- Fix: Never set I-am-a-thread bool to true IN the thread, dual-core machines could flip [FS#78] (r5977)
- Fix: Town-growth removed houses under construction to make way for road; unwanted behaviour [FS#49] (r5970)
- Fix: Cloned toad vehicles are not refitted to correct cargo [FS#275] (r5917)
- Fix: Bugfix for errors in FindNearestHangar function in aircraft_cmd.c [FS#235] (r5914)
- Fix: Sort order for produced amount and transported percentage was reversed in the industry list (r5912)
- Fix: Changing patch settings through the console didn't accept on/off or true/false [FS#170] (r5903)
- Fix: Differing price calculation for tunnels depending on starting point [FS#253] (r5901)
- Fix: Goto sepot not always working for road vehicles [FS#249] (r5898)
- Fix: Bus trying to service in depot of other company [SF1519167] (r5897)
- Fix: If vehicles break down and service is turned off, the vehicles failed to enter any depots; now they will quickly go to a depot if set to be replaced (r5888)
- Fix: Incomplete removal of player owned property due to lack of money [FS#273] (r5886)
- Fix: < > boxes in patch-settings didn't grey out when they hit the limit of their range (r5714)
- Fix: Check the configuration file for valid values and clamp them to their ingame minimum/maximum [SF1288024] (r3726)
- Fix: Remove the restriction that the 'patch' console command can only be run from network games [SF1366446] (r3723)
0.4.8 (2006-08-12)
------------------------------------------------------------------------
- Fix: A ship in a depot must be stopped before it can be cloned.
- Fix: After changing directory in 'Play Scenario', the default scenarios didn't show up in 'New Game'
0.4.8-RC2 (2006-07-31)
------------------------------------------------------------------------
- Feature: Add Italian town names as we have an official Italian translation
- Codechange: Verify the presence of music files in the gm/ folder. This should also solve some 100% CPU buildup for some users.
- Fix: Certain combinations of trains crash when moved around inside the depot.
@@ -350,10 +14,8 @@
- Fix: UDP sockets were used even if network-availability was set to false
- Fix: Crash when trying to build a vehicle type that is set to a max of zero
0.4.8-RC1 (2006-06-28)
------------------------------------------------------------------------
- Feature: Add Turkish town names as we have an official Turkish translation
- Feature: Add a fully optional configure script that is a wrapper around the cumbersome makefile.config
- Codechange: [NPF] Disable NPF totally for ships as it wholly kills performance (blathijs). Only for 0.4/ branch and 0.4.8.
@@ -411,7 +73,6 @@
- Fix: [NPF] Don't mark tiles when debugging in multiplayer, this will cause desyncs
- Fix: Several fixes to chatbox code, mainly plug a buffer overflow
0.4.7 (2006-03-26)
------------------------------------------------------------------------
@@ -420,7 +81,6 @@
- Fix: Allow unused wagons to have their first cache set. Fixes faulty cache-warning message and noticably speeds up depot operations (r4094)
- Fix: [NPF] Trains & busses were unable to find a route when leaving a depot or bus stop. (r4072)
0.4.6 (2006-03-22)
------------------------------------------------------------------------
@@ -456,7 +116,6 @@
- Fix: [win32] Add directives to allow Visual Studio 2005 compilation. (r3950)
- Fix: [ 1415782 ] crash in string code with openbsd/zaurus; alignment issues (r3948)
0.4.5 (2006-01-31)
------------------------------------------------------------------------
@@ -1198,7 +857,6 @@
0.3.2.1 (2004-05-23)
------------------------------------------------------------------------
- Fix: use english.lng by default
- Fix: No bridges available in 1920
- Fix: czech file was missing
@@ -1207,7 +865,6 @@
0.3.2 (2004-05-22)
------------------------------------------------------------------------
- Feature: HP for trians limited to 16bit int
- Feature: added Czech translation
- Feature: train refitting
@@ -1266,7 +923,6 @@
0.3.1 (2004-04-26)
------------------------------------------------------------------------
- Fix: shift+arrows keys scrolls faster (by pasky)
- Feature: bridge pillars for higher bridges
- Fix: [ 941880 ] "monorail in 1985" which allowed you to build monorail/maglev at any year
@@ -1307,9 +963,7 @@
- Fix: buffer overflow caused by too long string in english.lng
- Fix: destroying things with no money
0.3.0 (2004-04-14)
------------------------------------------------------------------------
- Change: don't slow down trains as much on hills
- Fix: aircraft terminal wasn't properly freed if aircraft crashed
- Fix: fixed station acceptance bug
@@ -1384,9 +1038,7 @@
- Feature: (OSX build) distribution now uses Apples package system for easier updates
- Feature: (OSX build) Application is now a proper bundle application
0.2.1 (2004-04-04)
------------------------------------------------------------------------
- Fix: copy orders crashed if you clicked on a wagon
- Feature: 'A' hotkey now always opens autorail
- Change: Moved autorail button
@@ -1396,9 +1048,8 @@
- Feature: Hotkeys 1-9 can be used to build a bridge in the bridge window
- Feature: Added more hotkeys in the road build window
0.2 (2004-04-03)
------------------------------------------------------------------------
- Feature: autoscroll (only works to left/right)
- Feature: train checkpoints, instead of ttdpatch's nonstop handling
- Feature: ttdpatch compatible nonstop handling
@@ -1456,9 +1107,8 @@
- Feature: on-the-fly language selection
- Feature: load old premade ttd maps (must be renamed to .sv1 extension)
0.1.4
0.1.4 (2004-03-25)
------------------------------------------------------------------------
- Feature: crash submit system on win32
- Fix: train smoke clouds
- Fix: train engine sounds
@@ -1496,9 +1146,8 @@
- Fix: Get remaining disk space on most Unix-en (rob)
- Fix: screen went black when resizing
0.1.3
0.1.3 (2004-03-18)
------------------------------------------------------------------------
- Fixed message options window
- Fixed company takeover/purchase
- Feature: Improved mouse scroll zooming
@@ -1519,16 +1168,14 @@
- Possibility to use either semaphores or signals (Ctrl key)
- Limited the scrolling rate for year selector in scenario editor
0.1.2
0.1.2 (2004-03-15)
------------------------------------------------------------------------
- Mouse wheel can be used to zoom in out on win32 (ludde)
- Implemented some support for resizing the window dynamically in win32 (ludde)
- Fixed tunnel mouse icon for maglev and monorail
0.1.1
0.1.1 (2004-03-14)
------------------------------------------------------------------------
- Preliminary presignal support
- Added external MIDI driver for unix version (by robertnorris)
- Added DirectMusic driver for Win32 version

View File

@@ -2,8 +2,6 @@
#include "stdafx.h"
#include "openttd.h"
#include "clear_map.h"
#include "rail_map.h"
#include "table/strings.h"
#include "functions.h"
#include "map.h"
@@ -14,9 +12,6 @@
#include "tunnel_map.h"
#include "variables.h"
#include "table/sprites.h"
#include "unmovable_map.h"
#include "genworld.h"
#include "industry.h"
typedef struct TerraformerHeightMod {
TileIndex tile;
@@ -74,7 +69,7 @@ static void TerraformAddDirtyTile(TerraformerState *ts, TileIndex tile)
if (count >= 625) return;
for (t = ts->tile_table; count != 0; count--,t++) {
for(t = ts->tile_table; count != 0; count--,t++) {
if (*t == tile) return;
}
@@ -136,9 +131,9 @@ static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode)
}
}
ret = DoCommand(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
ret = DoCommandByTile(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR);
if (CmdFailed(ret)) {
if (ret == CMD_ERROR) {
_terraform_err_tile = tile;
return -1;
}
@@ -181,7 +176,8 @@ static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height
for (;;) {
if (count == 0) {
if (ts->modheight_count >= 576) return false;
if (ts->modheight_count >= 576)
return false;
ts->modheight_count++;
break;
}
@@ -206,7 +202,7 @@ static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height
{ 0, -2}
};
for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
for(ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) {
tile += ToTileIndexDiff(*ttm);
r = TerraformGetHeightOfTile(ts, tile);
@@ -221,14 +217,14 @@ static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height
}
/** Terraform land
* @param tile tile to terraform
* @param x,y coordinates to terraform
* @param p1 corners to terraform.
* @param p2 direction; eg up or down
*/
int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
int32 CmdTerraformLand(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
TerraformerState ts;
TileIndex t;
TileIndex tile;
int direction;
TerraformerHeightMod modheight_data[576];
@@ -245,35 +241,33 @@ int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
ts.modheight = modheight_data;
ts.tile_table = tile_table_data;
tile = TileVirtXY(x, y);
/* Make an extra check for map-bounds cause we add tiles to the originating tile */
if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR;
if (p1 & 1) {
t = tile + TileDiffXY(1, 0);
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
return CMD_ERROR;
}
if (!TerraformTileHeight(&ts, tile + TileDiffXY(1, 0),
TileHeight(tile + TileDiffXY(1, 0)) + direction))
return CMD_ERROR;
}
if (p1 & 2) {
t = tile + TileDiffXY(1, 1);
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
return CMD_ERROR;
}
if (!TerraformTileHeight(&ts, tile + TileDiffXY(1, 1),
TileHeight(tile + TileDiffXY(1, 1)) + direction))
return CMD_ERROR;
}
if (p1 & 4) {
t = tile + TileDiffXY(0, 1);
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
return CMD_ERROR;
}
if (!TerraformTileHeight(&ts, tile + TileDiffXY(0, 1),
TileHeight(tile + TileDiffXY(0, 1)) + direction))
return CMD_ERROR;
}
if (p1 & 8) {
t = tile + TileDiffXY(0, 0);
if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) {
return CMD_ERROR;
}
if (!TerraformTileHeight(&ts, tile + TileDiffXY(0, 0),
TileHeight(tile + TileDiffXY(0, 0)) + direction))
return CMD_ERROR;
}
if (direction == -1) {
@@ -305,7 +299,7 @@ int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
int count;
TileIndex *ti = ts.tile_table;
for (count = ts.tile_table_count; count != 0; count--, ti++) {
DoCommand(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
DoCommandByTile(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
}
}
@@ -337,17 +331,16 @@ int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
/** Levels a selected (rectangle) area of land
* @param tile end tile of area-drag
* @param x,y end tile of area-drag
* @param p1 start tile of area drag
* @param p2 unused
*/
int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
int32 CmdLevelLand(int ex, int ey, uint32 flags, uint32 p1, uint32 p2)
{
int size_x, size_y;
int ex;
int ey;
int sx, sy;
uint h, curh;
TileIndex tile;
int32 ret, cost, money;
if (p1 >= MapSize()) return CMD_ERROR;
@@ -357,9 +350,9 @@ int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
// remember level height
h = TileHeight(p1);
ex >>= 4; ey >>= 4;
// make sure sx,sy are smaller than ex,ey
ex = TileX(tile);
ey = TileY(tile);
sx = TileX(p1);
sy = TileY(p1);
if (ex < sx) intswap(ex, sx);
@@ -375,7 +368,7 @@ int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) {
curh = TileHeight(tile2);
while (curh != h) {
ret = DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
ret = DoCommandByTile(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
if (CmdFailed(ret)) break;
cost += ret;
@@ -384,7 +377,7 @@ int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
_additional_cash_required = ret;
return cost - ret;
}
DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
DoCommandByTile(tile2, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
}
curh += (curh > h) ? -1 : 1;
@@ -396,28 +389,33 @@ int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
/** Purchase a land area. Actually you only purchase one tile, so
* the name is a bit confusing ;p
* @param tile the tile the player is purchasing
* @param x,y the tile the player is purchasing
* @param p1 unused
* @param p2 unused
*/
int32 CmdPurchaseLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
int32 CmdPurchaseLandArea(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
TileIndex tile;
int32 cost;
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
tile = TileVirtXY(x, y);
if (!EnsureNoVehicle(tile)) return CMD_ERROR;
if (IsOwnedLandTile(tile) && IsTileOwner(tile, _current_player)) {
if (IsTileType(tile, MP_UNMOVABLE) && _m[tile].m5 == 3 &&
IsTileOwner(tile, _current_player))
return_cmd_error(STR_5807_YOU_ALREADY_OWN_IT);
}
cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
cost = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (CmdFailed(cost)) return CMD_ERROR;
if (flags & DC_EXEC) {
MakeOwnedLand(tile, _current_player);
MarkTileDirtyByTile(tile);
ModifyTile(tile,
MP_SETTYPE(MP_UNMOVABLE) | MP_MAPOWNER_CURRENT | MP_MAP5,
3 /* map5 */
);
}
return cost + _price.purchase_land * 10;
@@ -426,45 +424,66 @@ int32 CmdPurchaseLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
static int32 ClearTile_Clear(TileIndex tile, byte flags)
{
static const int32 null = 0;
static const int32* clear_price_table[] = {
&null,
&_price.clear_1,
&_price.clear_1,
&_price.clear_1,
&_price.purchase_land,
&_price.purchase_land,
&_price.purchase_land,
&_price.purchase_land,
&_price.clear_2,
&_price.clear_2,
&_price.clear_2,
&_price.clear_2,
&_price.clear_3,
&_price.clear_3,
&_price.clear_3,
&_price.clear_3,
&_price.purchase_land,
&_price.purchase_land,
&_price.clear_2, // XXX unused?
&_price.purchase_land,
&_price.purchase_land,
&_price.purchase_land,
&_price.purchase_land,
&_price.purchase_land,
&_price.purchase_land,
&_price.clear_2,
&_price.clear_2,
&_price.clear_2,
&_price.clear_2,
};
int32 price;
if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
price = 0;
} else {
price = *clear_price_table[GetClearGround(tile)];
}
const int32 *price = clear_price_table[GB(_m[tile].m5, 0, 5)];
if (flags & DC_EXEC) DoClearSquare(tile);
return price;
return *price;
}
/** Sell a land area. Actually you only sell one tile, so
* the name is a bit confusing ;p
* @param tile the tile the player is selling
* @param x,y the tile the player is selling
* @param p1 unused
* @param p2 unused
*/
int32 CmdSellLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
int32 CmdSellLandArea(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
TileIndex tile;
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
if (!IsOwnedLandTile(tile)) return CMD_ERROR;
tile = TileVirtXY(x, y);
if (!IsTileType(tile, MP_UNMOVABLE) || _m[tile].m5 != 3) return CMD_ERROR;
if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) return CMD_ERROR;
if (!EnsureNoVehicle(tile)) return CMD_ERROR;
if (flags & DC_EXEC) DoClearSquare(tile);
if (flags & DC_EXEC)
DoClearSquare(tile);
return - _price.purchase_land * 2;
}
@@ -480,7 +499,7 @@ void DrawClearLandTile(const TileInfo *ti, byte set)
void DrawHillyLandTile(const TileInfo *ti)
{
if (ti->tileh != SLOPE_FLAT) {
if (ti->tileh != 0) {
DrawGroundSprite(SPR_FLAT_ROUGH_LAND + _tileh_to_sprite[ti->tileh]);
} else {
DrawGroundSprite(_landscape_clear_sprites[GB(ti->x ^ ti->y, 4, 3)]);
@@ -489,64 +508,62 @@ void DrawHillyLandTile(const TileInfo *ti)
void DrawClearLandFence(const TileInfo *ti)
{
byte m4 = _m[ti->tile].m4;
byte z = ti->z;
if (ti->tileh & SLOPE_S) {
z += TILE_HEIGHT;
if (ti->tileh == SLOPE_STEEP_S) z += TILE_HEIGHT;
if (ti->tileh & 2) {
z += 8;
if (ti->tileh == 0x17) z += 8;
}
if (GetFenceSW(ti->tile) != 0) {
DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSW(ti->tile) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z);
if (GB(m4, 5, 3) != 0) {
DrawGroundSpriteAt(_clear_land_fence_sprites_1[GB(m4, 5, 3) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z);
}
if (GetFenceSE(ti->tile) != 0) {
DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSE(ti->tile) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z);
if (GB(m4, 2, 3) != 0) {
DrawGroundSpriteAt(_clear_land_fence_sprites_1[GB(m4, 2, 3) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z);
}
}
static void DrawTile_Clear(TileInfo *ti)
{
switch (GetClearGround(ti->tile)) {
case CLEAR_GRASS:
DrawClearLandTile(ti, GetClearDensity(ti->tile));
break;
switch (GB(ti->map5, 2, 3)) {
case 0:
DrawClearLandTile(ti, GB(ti->map5, 0, 2));
break;
case CLEAR_ROUGH:
DrawHillyLandTile(ti);
break;
case 1:
DrawHillyLandTile(ti);
break;
case CLEAR_ROCKS:
DrawGroundSprite(SPR_FLAT_ROCKY_LAND_1 + _tileh_to_sprite[ti->tileh]);
break;
case 2:
DrawGroundSprite(SPR_FLAT_ROCKY_LAND_1 + _tileh_to_sprite[ti->tileh]);
break;
case CLEAR_FIELDS:
DrawGroundSprite(_clear_land_sprites_1[GetFieldType(ti->tile)] + _tileh_to_sprite[ti->tileh]);
break;
case 3:
DrawGroundSprite(_clear_land_sprites_1[GB(_m[ti->tile].m3, 0, 4)] + _tileh_to_sprite[ti->tileh]);
break;
case CLEAR_SNOW:
DrawGroundSprite(_clear_land_sprites_2[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]);
break;
case 4:
DrawGroundSprite(_clear_land_sprites_2[GB(ti->map5, 0, 2)] + _tileh_to_sprite[ti->tileh]);
break;
case CLEAR_DESERT:
DrawGroundSprite(_clear_land_sprites_3[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]);
break;
case 5:
DrawGroundSprite(_clear_land_sprites_3[GB(ti->map5, 0, 2)] + _tileh_to_sprite[ti->tileh]);
break;
}
DrawClearLandFence(ti);
}
static uint GetSlopeZ_Clear(TileIndex tile, uint x, uint y)
static uint GetSlopeZ_Clear(const TileInfo* ti)
{
uint z;
uint tileh = GetTileSlope(tile, &z);
return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
return GetPartialZ(ti->x & 0xF, ti->y & 0xF, ti->tileh) + ti->z;
}
static Slope GetSlopeTileh_Clear(TileIndex tile, Slope tileh)
static uint GetSlopeTileh_Clear(const TileInfo *ti)
{
return tileh;
return ti->tileh;
}
static void GetAcceptedCargo_Clear(TileIndex tile, AcceptedCargo ac)
@@ -565,30 +582,56 @@ void TileLoopClearHelper(TileIndex tile)
byte neighbour;
TileIndex dirty = INVALID_TILE;
self = (IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS));
switch (GetTileType(tile)) {
case MP_CLEAR:
self = (GB(_m[tile].m5, 0, 5) == 15);
break;
neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS));
if (GetFenceSW(tile) == 0) {
default:
self = 0;
break;
}
switch (GetTileType(TILE_ADDXY(tile, 1, 0))) {
case MP_CLEAR:
neighbour = (GB(_m[TILE_ADDXY(tile, 1, 0)].m5, 0, 5) == 15);
break;
default:
neighbour = 0;
break;
}
if (GB(_m[tile].m4, 5, 3) == 0) {
if (self != neighbour) {
SetFenceSW(tile, 3);
SB(_m[tile].m4, 5, 3, 3);
dirty = tile;
}
} else {
if (self == 0 && neighbour == 0) {
SetFenceSW(tile, 0);
SB(_m[tile].m4, 5, 3, 0);
dirty = tile;
}
}
neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS));
if (GetFenceSE(tile) == 0) {
switch (GetTileType(TILE_ADDXY(tile, 0, 1))) {
case MP_CLEAR:
neighbour = (GB(_m[TILE_ADDXY(tile, 0, 1)].m5, 0, 5) == 15);
break;
default:
neighbour = 0;
break;
}
if (GB(_m[tile].m4, 2, 3) == 0) {
if (self != neighbour) {
SetFenceSE(tile, 3);
SB(_m[tile].m4, 2, 3, 3);
dirty = tile;
}
} else {
if (self == 0 && neighbour == 0) {
SetFenceSE(tile, 0);
SB(_m[tile].m4, 2, 3, 0);
dirty = tile;
}
}
@@ -600,43 +643,78 @@ void TileLoopClearHelper(TileIndex tile)
/* convert into snowy tiles */
static void TileLoopClearAlps(TileIndex tile)
{
int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT;
int k;
byte m5,tmp;
if (k < 0) { // well below the snow line
if (!IsClearGround(tile, CLEAR_SNOW)) return;
if (GetClearDensity(tile) == 0) SetClearGroundDensity(tile, CLEAR_GRASS, 3);
/* distance from snow line, in steps of 8 */
k = GetTileZ(tile) - _opt.snow_line;
m5 = _m[tile].m5 & 0x1C;
tmp = _m[tile].m5 & 3;
if (k < -8) {
/* snow_m2_down */
if (m5 != 0x10)
return;
if (tmp == 0)
m5 = 3;
} else if (k == -8) {
/* snow_m1 */
if (m5 != 0x10) {
m5 = 0x10;
} else if (tmp != 0) {
m5 = (tmp - 1) + 0x10;
} else
return;
} else if (k < 8) {
/* snow_0 */
if (m5 != 0x10) {
m5 = 0x10;
} else if (tmp != 1) {
m5 = 1;
if (tmp != 0)
m5 = tmp - 1;
m5 += 0x10;
} else
return;
} else if (k == 8) {
/* snow_p1 */
if (m5 != 0x10) {
m5 = 0x10;
} else if (tmp != 2) {
m5 = 2;
if (tmp <= 2)
m5 = tmp + 1;
m5 += 0x10;
} else
return;
} else {
if (!IsClearGround(tile, CLEAR_SNOW)) {
SetClearGroundDensity(tile, CLEAR_SNOW, 0);
} else {
uint density = min((uint)k / TILE_HEIGHT, 3);
if (GetClearDensity(tile) < density) {
AddClearDensity(tile, 1);
} else if (GetClearDensity(tile) > density) {
AddClearDensity(tile, -1);
} else {
return;
}
}
/* snow_p2_up */
if (m5 != 0x10) {
m5 = 0x10;
} else if (tmp != 3) {
m5 = tmp + 1 + 0x10;
} else
return;
}
_m[tile].m5 = m5;
MarkTileDirtyByTile(tile);
}
static void TileLoopClearDesert(TileIndex tile)
{
if (IsClearGround(tile, CLEAR_DESERT)) return;
if ((_m[tile].m5 & 0x1C) == 0x14) return;
if (GetTropicZone(tile) == TROPICZONE_DESERT) {
SetClearGroundDensity(tile, CLEAR_DESERT, 3);
if (GetMapExtraBits(tile) == 1) {
_m[tile].m5 = 0x17;
} else {
if (GetTropicZone(tile + TileDiffXY( 1, 0)) != TROPICZONE_DESERT &&
GetTropicZone(tile + TileDiffXY(-1, 0)) != TROPICZONE_DESERT &&
GetTropicZone(tile + TileDiffXY( 0, 1)) != TROPICZONE_DESERT &&
GetTropicZone(tile + TileDiffXY( 0, -1)) != TROPICZONE_DESERT)
if (GetMapExtraBits(tile + TileDiffXY( 1, 0)) != 1 &&
GetMapExtraBits(tile + TileDiffXY(-1, 0)) != 1 &&
GetMapExtraBits(tile + TileDiffXY( 0, 1)) != 1 &&
GetMapExtraBits(tile + TileDiffXY( 0, -1)) != 1)
return;
SetClearGroundDensity(tile, CLEAR_DESERT, 1);
_m[tile].m5 = 0x15;
}
MarkTileDirtyByTile(tile);
@@ -644,93 +722,81 @@ static void TileLoopClearDesert(TileIndex tile)
static void TileLoop_Clear(TileIndex tile)
{
byte m5,m3;
TileLoopClearHelper(tile);
switch (_opt.landscape) {
case LT_DESERT: TileLoopClearDesert(tile); break;
case LT_HILLY: TileLoopClearAlps(tile); break;
if (_opt.landscape == LT_DESERT) {
TileLoopClearDesert(tile);
} else if (_opt.landscape == LT_HILLY) {
TileLoopClearAlps(tile);
}
switch (GetClearGround(tile)) {
case CLEAR_GRASS:
if (GetClearDensity(tile) == 3) return;
m5 = _m[tile].m5;
if ((m5 & 0x1C) == 0x10 || (m5 & 0x1C) == 0x14) return;
if (_game_mode != GM_EDITOR) {
if (GetClearCounter(tile) < 7) {
AddClearCounter(tile, 1);
return;
} else {
SetClearCounter(tile, 0);
AddClearDensity(tile, 1);
}
} else {
SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3);
}
break;
if ((m5 & 0x1C) != 0xC) {
if ((m5 & 3) == 3) return;
case CLEAR_FIELDS: {
uint field_type;
if (_game_mode == GM_EDITOR) return;
if (GetClearCounter(tile) < 7) {
AddClearCounter(tile, 1);
if (_game_mode != GM_EDITOR) {
m5 += 0x20;
if (m5 >= 0x20) {
// Didn't overflow
_m[tile].m5 = m5;
return;
} else {
SetClearCounter(tile, 0);
}
if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) {
/* This farmfield is no longer farmfield, so make it grass again */
MakeClear(tile, CLEAR_GRASS, 2);
} else {
field_type = GetFieldType(tile);
field_type = (field_type < 8) ? field_type + 1 : 0;
SetFieldType(tile, field_type);
}
break;
/* did overflow, so continue */
} else {
m5 = (GB(Random(), 0, 8) > 21) ? 2 : 6;
}
default:
m5++;
} else if (_game_mode != GM_EDITOR) {
/* handle farm field */
m5 += 0x20;
if (m5 >= 0x20) {
// Didn't overflow
_m[tile].m5 = m5;
return;
}
/* overflowed */
m3 = _m[tile].m3 + 1;
assert( (m3 & 0xF) != 0);
if ( (m3 & 0xF) >= 9) /* NOTE: will not work properly if m3&0xF == 0xF */
m3 &= ~0xF;
_m[tile].m3 = m3;
}
_m[tile].m5 = m5;
MarkTileDirtyByTile(tile);
}
void GenerateClearTile(void)
{
uint i, gi;
uint i;
TileIndex tile;
/* add rough tiles */
/* add hills */
i = ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
gi = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
SetGeneratingWorldProgress(GWP_ROUGH_ROCKY, gi + i);
do {
IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
tile = RandomTile();
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
if (IsTileType(tile, MP_CLEAR)) SB(_m[tile].m5, 2, 2, 1);
} while (--i);
/* add rocky tiles */
i = gi;
/* add grey squares */
i = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
do {
uint32 r = Random();
tile = RandomTileSeed(r);
IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) {
if (IsTileType(tile, MP_CLEAR)) {
uint j = GB(r, 16, 4) + 5;
for (;;) {
for(;;) {
TileIndex tile_new;
SetClearGroundDensity(tile, CLEAR_ROCKS, 3);
SB(_m[tile].m5, 2, 2, 2);
do {
if (--j == 0) goto get_out;
tile_new = tile + TileOffsByDiagDir(GB(Random(), 0, 2));
} while (!IsTileType(tile_new, MP_CLEAR) || IsClearGround(tile_new, CLEAR_DESERT));
tile_new = tile + TileOffsByDir(GB(Random(), 0, 2));
} while (!IsTileType(tile_new, MP_CLEAR));
tile = tile_new;
}
get_out:;
@@ -749,21 +815,24 @@ static uint32 GetTileTrackStatus_Clear(TileIndex tile, TransportType mode)
}
static const StringID _clear_land_str[] = {
STR_080D_GRASS,
STR_080B_ROUGH_LAND,
STR_080A_ROCKS,
STR_080E_FIELDS,
STR_080F_SNOW_COVERED_LAND,
STR_0810_DESERT
STR_0810_DESERT,
0,
0,
STR_080C_BARE_LAND,
STR_080D_GRASS,
STR_080D_GRASS,
STR_080D_GRASS,
};
static void GetTileDesc_Clear(TileIndex tile, TileDesc *td)
{
if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
td->str = STR_080C_BARE_LAND;
} else {
td->str = _clear_land_str[GetClearGround(tile)];
}
uint i = GB(_m[tile].m5, 2, 3);
if (i == 0) i = GB(_m[tile].m5, 0, 2) + 8;
td->str = _clear_land_str[i - 1];
td->owner = GetTileOwner(tile);
}
@@ -774,21 +843,22 @@ static void ChangeTileOwner_Clear(TileIndex tile, PlayerID old_player, PlayerID
void InitializeClearLand(void)
{
_opt.snow_line = _patches.snow_line_height * TILE_HEIGHT;
_opt.snow_line = _patches.snow_line_height * 8;
}
const TileTypeProcs _tile_type_clear_procs = {
DrawTile_Clear, /* draw_tile_proc */
GetSlopeZ_Clear, /* get_slope_z_proc */
ClearTile_Clear, /* clear_tile_proc */
GetAcceptedCargo_Clear, /* get_accepted_cargo_proc */
GetTileDesc_Clear, /* get_tile_desc_proc */
GetTileTrackStatus_Clear, /* get_tile_track_status_proc */
ClickTile_Clear, /* click_tile_proc */
AnimateTile_Clear, /* animate_tile_proc */
TileLoop_Clear, /* tile_loop_clear */
ChangeTileOwner_Clear, /* change_tile_owner_clear */
NULL, /* get_produced_cargo_proc */
NULL, /* vehicle_enter_tile_proc */
GetSlopeTileh_Clear, /* get_slope_tileh_proc */
DrawTile_Clear, /* draw_tile_proc */
GetSlopeZ_Clear, /* get_slope_z_proc */
ClearTile_Clear, /* clear_tile_proc */
GetAcceptedCargo_Clear, /* get_accepted_cargo_proc */
GetTileDesc_Clear, /* get_tile_desc_proc */
GetTileTrackStatus_Clear, /* get_tile_track_status_proc */
ClickTile_Clear, /* click_tile_proc */
AnimateTile_Clear, /* animate_tile_proc */
TileLoop_Clear, /* tile_loop_clear */
ChangeTileOwner_Clear, /* change_tile_owner_clear */
NULL, /* get_produced_cargo_proc */
NULL, /* vehicle_enter_tile_proc */
NULL, /* vehicle_leave_tile_proc */
GetSlopeTileh_Clear, /* get_slope_tileh_proc */
};

View File

@@ -1,145 +0,0 @@
/* $Id$ */
#ifndef CLEAR_MAP_H
#define CLEAR_MAP_H
#include "macros.h"
#include "tile.h"
/* ground type, m5 bits 2...4
* valid densities (bits 0...1) in comments after the enum
*/
typedef enum ClearGround {
CLEAR_GRASS = 0, // 0-3
CLEAR_ROUGH = 1, // 3
CLEAR_ROCKS = 2, // 3
CLEAR_FIELDS = 3, // 3
CLEAR_SNOW = 4, // 0-3
CLEAR_DESERT = 5 // 1,3
} ClearGround;
static inline ClearGround GetClearGround(TileIndex t)
{
assert(IsTileType(t, MP_CLEAR));
return GB(_m[t].m5, 2, 3);
}
static inline bool IsClearGround(TileIndex t, ClearGround ct)
{
return GetClearGround(t) == ct;
}
static inline uint GetClearDensity(TileIndex t)
{
assert(IsTileType(t, MP_CLEAR));
return GB(_m[t].m5, 0, 2);
}
static inline void AddClearDensity(TileIndex t, int d)
{
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
_m[t].m5 += d;
}
static inline uint GetClearCounter(TileIndex t)
{
assert(IsTileType(t, MP_CLEAR));
return GB(_m[t].m5, 5, 3);
}
static inline void AddClearCounter(TileIndex t, int c)
{
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
_m[t].m5 += c << 5;
}
static inline void SetClearCounter(TileIndex t, uint c)
{
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
SB(_m[t].m5, 5, 3, c);
}
/* Sets type and density in one go, also sets the counter to 0 */
static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint density)
{
assert(IsTileType(t, MP_CLEAR)); // XXX incomplete
_m[t].m5 = 0 << 5 | type << 2 | density;
}
static inline uint GetFieldType(TileIndex t)
{
assert(GetClearGround(t) == CLEAR_FIELDS);
return GB(_m[t].m3, 0, 4);
}
static inline void SetFieldType(TileIndex t, uint f)
{
assert(GetClearGround(t) == CLEAR_FIELDS); // XXX incomplete
SB(_m[t].m3, 0, 4, f);
}
static inline uint16 GetIndustryIndexOfField(TileIndex t)
{
assert(GetClearGround(t) == CLEAR_FIELDS);
return _m[t].m2;
}
static inline void SetIndustryIndexOfField(TileIndex t, uint16 i)
{
assert(GetClearGround(t) == CLEAR_FIELDS);
_m[t].m2 = i;
}
/* Is used by tree tiles, too */
static inline uint GetFenceSE(TileIndex t)
{
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES));
return GB(_m[t].m4, 2, 3);
}
static inline void SetFenceSE(TileIndex t, uint h)
{
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); // XXX incomplete
SB(_m[t].m4, 2, 3, h);
}
static inline uint GetFenceSW(TileIndex t)
{
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES));
return GB(_m[t].m4, 5, 3);
}
static inline void SetFenceSW(TileIndex t, uint h)
{
assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); // XXX incomplete
SB(_m[t].m4, 5, 3, h);
}
static inline void MakeClear(TileIndex t, ClearGround g, uint density)
{
SetTileType(t, MP_CLEAR);
SetTileOwner(t, OWNER_NONE);
_m[t].m2 = 0;
_m[t].m3 = 0;
_m[t].m4 = 0 << 5 | 0 << 2;
SetClearGroundDensity(t, g, density);
}
static inline void MakeField(TileIndex t, uint field_type, uint16 industry)
{
SetTileType(t, MP_CLEAR);
SetTileOwner(t, OWNER_NONE);
_m[t].m2 = industry;
_m[t].m3 = field_type;
_m[t].m4 = 0 << 5 | 0 << 2;
SetClearGroundDensity(t, CLEAR_FIELDS, 3);
}
#endif /* CLEAR_MAP_H */

103
command.c
View File

@@ -10,11 +10,10 @@
#include "player.h"
#include "network.h"
#include "variables.h"
#include "genworld.h"
const char* _cmd_text = NULL;
#define DEF_COMMAND(yyyy) int32 yyyy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
#define DEF_COMMAND(yyyy) int32 yyyy(int x, int y, uint32 flags, uint32 p1, uint32 p2)
DEF_COMMAND(CmdBuildRailroadTrack);
DEF_COMMAND(CmdRemoveRailroadTrack);
@@ -114,7 +113,6 @@ DEF_COMMAND(CmdStartStopRoadVeh);
DEF_COMMAND(CmdSellRoadVeh);
DEF_COMMAND(CmdSendRoadVehToDepot);
DEF_COMMAND(CmdTurnRoadVeh);
DEF_COMMAND(CmdRefitRoadVeh);
DEF_COMMAND(CmdPause);
@@ -138,7 +136,6 @@ DEF_COMMAND(CmdBuildShip);
DEF_COMMAND(CmdSendShipToDepot);
DEF_COMMAND(CmdRefitShip);
DEF_COMMAND(CmdOrderRefit);
DEF_COMMAND(CmdCloneOrder);
DEF_COMMAND(CmdClearArea);
@@ -157,12 +154,10 @@ DEF_COMMAND(CmdRefitRailVehicle);
DEF_COMMAND(CmdBuildSignalTrack);
DEF_COMMAND(CmdRemoveSignalTrack);
DEF_COMMAND(CmdSetAutoReplace);
DEF_COMMAND(CmdReplaceVehicle);
DEF_COMMAND(CmdCloneVehicle);
DEF_COMMAND(CmdMassStartStopVehicle);
DEF_COMMAND(CmdDepotSellAllVehicles);
DEF_COMMAND(CmdDepotMassAutoReplace);
/* The master command table */
static const Command _command_proc_table[] = {
@@ -250,7 +245,7 @@ static const Command _command_proc_table[] = {
{CmdSellRoadVeh, 0}, /* 69 */
{CmdSendRoadVehToDepot, 0}, /* 70 */
{CmdTurnRoadVeh, 0}, /* 71 */
{CmdRefitRoadVeh, 0}, /* 72 */
{NULL, 0}, /* 72 */
{CmdPause, CMD_SERVER}, /* 73 */
@@ -282,8 +277,8 @@ static const Command _command_proc_table[] = {
{NULL, 0}, /* 95 */
{NULL, 0}, /* 96 */
{NULL, 0}, /* 97 */
{NULL, 0}, /* 98 */
{CmdOrderRefit, 0}, /* 98 */
{CmdCloneOrder, 0}, /* 99 */
{CmdClearArea, 0}, /* 100 */
@@ -304,11 +299,8 @@ static const Command _command_proc_table[] = {
{NULL, 0}, /* 112 */
{CmdGiveMoney, 0}, /* 113 */
{CmdChangePatchSetting, CMD_SERVER}, /* 114 */
{CmdSetAutoReplace, 0}, /* 115 */
{CmdCloneVehicle, 0}, /* 116 */
{CmdMassStartStopVehicle, 0}, /* 117 */
{CmdDepotSellAllVehicles, 0}, /* 118 */
{CmdDepotMassAutoReplace, 0}, /* 119 */
{CmdReplaceVehicle, 0}, /* 115 */
{CmdCloneVehicle, 0}, /* 116 */
};
/* This function range-checks a cmd, and checks if the cmd is not NULL */
@@ -321,21 +313,23 @@ bool IsValidCommand(uint cmd)
_command_proc_table[cmd].proc != NULL;
}
byte GetCommandFlags(uint cmd)
byte GetCommandFlags(uint cmd) {return _command_proc_table[cmd & 0xFF].flags;}
int32 DoCommandByTile(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
{
return _command_proc_table[cmd & 0xFF].flags;
return DoCommand(TileX(tile) * 16, TileY(tile) * 16, p1, p2, flags, procc);
}
static int _docommand_recursive;
int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
int32 DoCommand(int x, int y, uint32 p1, uint32 p2, uint32 flags, uint procc)
{
int32 res;
CommandProc *proc;
/* Do not even think about executing out-of-bounds tile-commands */
if (tile >= MapSize() || IsTileType(tile, MP_VOID)) {
if (TileVirtXY(x, y) >= MapSize()) {
_cmd_text = NULL;
return CMD_ERROR;
}
@@ -348,17 +342,15 @@ int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
// only execute the test call if it's toplevel, or we're not execing.
if (_docommand_recursive == 1 || !(flags & DC_EXEC) || (flags & DC_FORCETEST) ) {
res = proc(tile, flags & ~DC_EXEC, p1, p2);
res = proc(x, y, flags&~DC_EXEC, p1, p2);
if (CmdFailed(res)) {
if (res & 0xFFFF) _error_message = res & 0xFFFF;
goto error;
}
if (_docommand_recursive == 1 &&
!(flags & DC_QUERY_COST) &&
res != 0 &&
!CheckPlayerHasMoney(res)) {
goto error;
if (_docommand_recursive == 1) {
if (!(flags&DC_QUERY_COST) && res != 0 && !CheckPlayerHasMoney(res))
goto error;
}
if (!(flags & DC_EXEC)) {
@@ -370,7 +362,7 @@ int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
/* Execute the command here. All cost-relevant functions set the expenses type
* themselves with "SET_EXPENSES_TYPE(...);" at the beginning of the function */
res = proc(tile, flags, p1, p2);
res = proc(x, y, flags, p1, p2);
if (CmdFailed(res)) {
if (res & 0xFFFF) _error_message = res & 0xFFFF;
error:
@@ -383,8 +375,8 @@ error:
if (--_docommand_recursive == 0) {
SubtractMoneyFromPlayer(res);
// XXX - Old AI hack which doesn't use DoCommandDP; update last build coord of player
if (tile != 0 && IsValidPlayer(_current_player)) {
GetPlayer(_current_player)->last_build_coordinate = tile;
if ( (x|y) != 0 && _current_player < MAX_PLAYERS) {
GetPlayer(_current_player)->last_build_coordinate = TileVirtXY(x, y);
}
}
@@ -395,7 +387,7 @@ error:
int32 GetAvailableMoneyForCommand(void)
{
PlayerID pid = _current_player;
if (!IsValidPlayer(pid)) return 0x7FFFFFFF; // max int
if (pid >= MAX_PLAYERS) return 0x7FFFFFFF; // max int
return GetPlayer(pid)->player_money;
}
@@ -407,13 +399,12 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
CommandProc *proc;
uint32 flags;
bool notest;
StringID error_part1;
int x = TileX(tile) * TILE_SIZE;
int y = TileY(tile) * TILE_SIZE;
int x = TileX(tile) * 16;
int y = TileY(tile) * 16;
/* Do not even think about executing out-of-bounds tile-commands */
if (tile >= MapSize() || IsTileType(tile, MP_VOID)) {
if (tile >= MapSize()) {
_cmd_text = NULL;
return false;
}
@@ -421,13 +412,13 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
assert(_docommand_recursive == 0);
_error_message = INVALID_STRING_ID;
error_part1 = GB(cmd, 16, 16);
_error_message_2 = cmd >> 16;
_additional_cash_required = 0;
/** Spectator has no rights except for the (dedicated) server which
* is/can be a spectator but as the server it can do anything */
if (_current_player == PLAYER_SPECTATOR && !_network_server) {
ShowErrorMessage(_error_message, error_part1, x, y);
/** Spectator has no rights except for the dedicated server which
* is a spectator but is the server, so can do anything */
if (_current_player == OWNER_SPECTATOR && !_network_dedicated) {
ShowErrorMessage(_error_message, _error_message_2, x, y);
_cmd_text = NULL;
return false;
}
@@ -463,16 +454,12 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
_docommand_recursive = 1;
// cost estimation only?
if (!IsGeneratingWorld() &&
_shift_pressed &&
IsLocalPlayer() &&
!(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR)) &&
(cmd & 0xFF) != CMD_PAUSE) {
if (_shift_pressed && IsLocalPlayer() && !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR))) {
// estimate the cost.
res = proc(tile, flags, p1, p2);
res = proc(x, y, flags, p1, p2);
if (CmdFailed(res)) {
if (res & 0xFFFF) _error_message = res & 0xFFFF;
ShowErrorMessage(_error_message, error_part1, x, y);
ShowErrorMessage(_error_message, _error_message_2, x, y);
} else {
ShowEstimatedCostOrIncome(res, x, y);
}
@@ -485,7 +472,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
// first test if the command can be executed.
res = proc(tile, flags, p1, p2);
res = proc(x,y, flags, p1, p2);
if (CmdFailed(res)) {
if (res & 0xFFFF) _error_message = res & 0xFFFF;
goto show_error;
@@ -499,14 +486,12 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
* send it to the command-queue and abort execution
* If we are a dedicated server temporarily switch local player, otherwise
* the other parties won't be able to execute our command and will desync.
* We also need to do this if the server's company has gone bankrupt
* @todo Rewrite (dedicated) server to something more than a dirty hack!
* @todo Rewrite dedicated server to something more than a dirty hack!
*/
if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
PlayerID pbck = _local_player;
if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = 0;
if (_network_dedicated) _local_player = 0;
NetworkSend_Command(tile, p1, p2, cmd, callback);
if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck;
if (_network_dedicated) _local_player = OWNER_SPECTATOR;
_docommand_recursive = 0;
_cmd_text = NULL;
return true;
@@ -514,14 +499,12 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
#endif /* ENABLE_NETWORK */
// update last build coordinate of player.
if (tile != 0 && IsValidPlayer(_current_player)) {
GetPlayer(_current_player)->last_build_coordinate = tile;
}
if ( tile != 0 && _current_player < MAX_PLAYERS) GetPlayer(_current_player)->last_build_coordinate = tile;
/* Actually try and execute the command. If no cost-type is given
* use the construction one */
_yearly_expenses_type = EXPENSES_CONSTRUCTION;
res2 = proc(tile, flags | DC_EXEC, p1, p2);
res2 = proc(x,y, flags|DC_EXEC, p1, p2);
// If notest is on, it means the result of the test can be different than
// the real command.. so ignore the test
@@ -537,10 +520,11 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
SubtractMoneyFromPlayer(res2);
if (IsLocalPlayer() && _game_mode != GM_EDITOR) {
if (res2 != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2);
if (res2 != 0)
ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2);
if (_additional_cash_required) {
SetDParam(0, _additional_cash_required);
ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x,y);
ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, _error_message_2, x,y);
if (res2 == 0) goto callb_err;
}
}
@@ -553,9 +537,8 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
show_error:
// show error message if the command fails?
if (IsLocalPlayer() && error_part1 != 0) {
ShowErrorMessage(_error_message, error_part1, x,y);
}
if (IsLocalPlayer() && _error_message_2 != 0)
ShowErrorMessage(_error_message, _error_message_2, x,y);
callb_err:
_docommand_recursive = 0;

232
command.h
View File

@@ -4,153 +4,149 @@
#define COMMAND_H
enum {
CMD_BUILD_RAILROAD_TRACK = 0,
CMD_REMOVE_RAILROAD_TRACK = 1,
CMD_BUILD_SINGLE_RAIL = 2,
CMD_REMOVE_SINGLE_RAIL = 3,
CMD_LANDSCAPE_CLEAR = 4,
CMD_BUILD_BRIDGE = 5,
CMD_BUILD_RAILROAD_STATION = 6,
CMD_BUILD_TRAIN_DEPOT = 7,
CMD_BUILD_SIGNALS = 8,
CMD_REMOVE_SIGNALS = 9,
CMD_TERRAFORM_LAND = 10,
CMD_PURCHASE_LAND_AREA = 11,
CMD_SELL_LAND_AREA = 12,
CMD_BUILD_TUNNEL = 13,
CMD_BUILD_RAILROAD_TRACK = 0,
CMD_REMOVE_RAILROAD_TRACK = 1,
CMD_BUILD_SINGLE_RAIL = 2,
CMD_REMOVE_SINGLE_RAIL = 3,
CMD_LANDSCAPE_CLEAR = 4,
CMD_BUILD_BRIDGE = 5,
CMD_BUILD_RAILROAD_STATION = 6,
CMD_BUILD_TRAIN_DEPOT = 7,
CMD_BUILD_SIGNALS = 8,
CMD_REMOVE_SIGNALS = 9,
CMD_TERRAFORM_LAND = 10,
CMD_PURCHASE_LAND_AREA = 11,
CMD_SELL_LAND_AREA = 12,
CMD_BUILD_TUNNEL = 13,
CMD_REMOVE_FROM_RAILROAD_STATION = 14,
CMD_CONVERT_RAIL = 15,
CMD_REMOVE_FROM_RAILROAD_STATION = 14,
CMD_CONVERT_RAIL = 15,
CMD_BUILD_TRAIN_WAYPOINT = 16,
CMD_RENAME_WAYPOINT = 17,
CMD_REMOVE_TRAIN_WAYPOINT = 18,
CMD_BUILD_TRAIN_WAYPOINT = 16,
CMD_RENAME_WAYPOINT = 17,
CMD_REMOVE_TRAIN_WAYPOINT = 18,
CMD_BUILD_ROAD_STOP = 21,
CMD_BUILD_LONG_ROAD = 23,
CMD_REMOVE_LONG_ROAD = 24,
CMD_BUILD_ROAD = 25,
CMD_REMOVE_ROAD = 26,
CMD_BUILD_ROAD_DEPOT = 27,
CMD_BUILD_ROAD_STOP = 21,
CMD_BUILD_LONG_ROAD = 23,
CMD_REMOVE_LONG_ROAD = 24,
CMD_BUILD_ROAD = 25,
CMD_REMOVE_ROAD = 26,
CMD_BUILD_ROAD_DEPOT = 27,
CMD_BUILD_AIRPORT = 29,
CMD_BUILD_AIRPORT = 29,
CMD_BUILD_DOCK = 30,
CMD_BUILD_DOCK = 30,
CMD_BUILD_SHIP_DEPOT = 31,
CMD_BUILD_BUOY = 32,
CMD_BUILD_SHIP_DEPOT = 31,
CMD_BUILD_BUOY = 32,
CMD_PLANT_TREE = 33,
CMD_PLANT_TREE = 33,
CMD_BUILD_RAIL_VEHICLE = 34,
CMD_MOVE_RAIL_VEHICLE = 35,
CMD_BUILD_RAIL_VEHICLE = 34,
CMD_MOVE_RAIL_VEHICLE = 35,
CMD_START_STOP_TRAIN = 36,
CMD_START_STOP_TRAIN = 36,
CMD_SELL_RAIL_WAGON = 38,
CMD_SELL_RAIL_WAGON = 38,
CMD_SEND_TRAIN_TO_DEPOT = 39,
CMD_FORCE_TRAIN_PROCEED = 40,
CMD_REVERSE_TRAIN_DIRECTION = 41,
CMD_TRAIN_GOTO_DEPOT = 39,
CMD_FORCE_TRAIN_PROCEED = 40,
CMD_REVERSE_TRAIN_DIRECTION = 41,
CMD_MODIFY_ORDER = 42,
CMD_SKIP_ORDER = 43,
CMD_DELETE_ORDER = 44,
CMD_INSERT_ORDER = 45,
CMD_MODIFY_ORDER = 42,
CMD_SKIP_ORDER = 43,
CMD_DELETE_ORDER = 44,
CMD_INSERT_ORDER = 45,
CMD_CHANGE_SERVICE_INT = 46,
CMD_CHANGE_SERVICE_INT = 46,
CMD_BUILD_INDUSTRY = 47,
CMD_BUILD_INDUSTRY = 47,
CMD_BUILD_COMPANY_HQ = 48,
CMD_SET_PLAYER_FACE = 49,
CMD_SET_PLAYER_COLOR = 50,
CMD_BUILD_COMPANY_HQ = 48,
CMD_SET_PLAYER_FACE = 49,
CMD_SET_PLAYER_COLOR = 50,
CMD_INCREASE_LOAN = 51,
CMD_DECREASE_LOAN = 52,
CMD_INCREASE_LOAN = 51,
CMD_DECREASE_LOAN = 52,
CMD_WANT_ENGINE_PREVIEW = 53,
CMD_WANT_ENGINE_PREVIEW = 53,
CMD_NAME_VEHICLE = 54,
CMD_RENAME_ENGINE = 55,
CMD_CHANGE_COMPANY_NAME = 56,
CMD_CHANGE_PRESIDENT_NAME = 57,
CMD_RENAME_STATION = 58,
CMD_NAME_VEHICLE = 54,
CMD_RENAME_ENGINE = 55,
CMD_CHANGE_COMPANY_NAME = 56,
CMD_CHANGE_PRESIDENT_NAME = 57,
CMD_RENAME_STATION = 58,
CMD_SELL_AIRCRAFT = 59,
CMD_START_STOP_AIRCRAFT = 60,
CMD_BUILD_AIRCRAFT = 61,
CMD_SEND_AIRCRAFT_TO_HANGAR = 62,
CMD_REFIT_AIRCRAFT = 64,
CMD_SELL_AIRCRAFT = 59,
CMD_START_STOP_AIRCRAFT = 60,
CMD_BUILD_AIRCRAFT = 61,
CMD_SEND_AIRCRAFT_TO_HANGAR = 62,
CMD_REFIT_AIRCRAFT = 64,
CMD_PLACE_SIGN = 65,
CMD_RENAME_SIGN = 66,
CMD_PLACE_SIGN = 65,
CMD_RENAME_SIGN = 66,
CMD_BUILD_ROAD_VEH = 67,
CMD_START_STOP_ROADVEH = 68,
CMD_SELL_ROAD_VEH = 69,
CMD_SEND_ROADVEH_TO_DEPOT = 70,
CMD_TURN_ROADVEH = 71,
CMD_REFIT_ROAD_VEH = 72,
CMD_BUILD_ROAD_VEH = 67,
CMD_START_STOP_ROADVEH = 68,
CMD_SELL_ROAD_VEH = 69,
CMD_SEND_ROADVEH_TO_DEPOT = 70,
CMD_TURN_ROADVEH = 71,
CMD_PAUSE = 73,
CMD_PAUSE = 73,
CMD_BUY_SHARE_IN_COMPANY = 74,
CMD_SELL_SHARE_IN_COMPANY = 75,
CMD_BUY_COMPANY = 76,
CMD_BUY_SHARE_IN_COMPANY = 74,
CMD_SELL_SHARE_IN_COMPANY = 75,
CMD_BUY_COMPANY = 76,
CMD_BUILD_TOWN = 77,
CMD_BUILD_TOWN = 77,
CMD_RENAME_TOWN = 80,
CMD_DO_TOWN_ACTION = 81,
CMD_RENAME_TOWN = 80,
CMD_DO_TOWN_ACTION = 81,
CMD_SET_ROAD_DRIVE_SIDE = 82,
CMD_SET_ROAD_DRIVE_SIDE = 82,
CMD_CHANGE_DIFFICULTY_LEVEL = 85,
CMD_CHANGE_DIFFICULTY_LEVEL = 85,
CMD_START_STOP_SHIP = 86,
CMD_SELL_SHIP = 87,
CMD_BUILD_SHIP = 88,
CMD_SEND_SHIP_TO_DEPOT = 89,
CMD_REFIT_SHIP = 91,
CMD_START_STOP_SHIP = 86,
CMD_SELL_SHIP = 87,
CMD_BUILD_SHIP = 88,
CMD_SEND_SHIP_TO_DEPOT = 89,
CMD_REFIT_SHIP = 91,
CMD_ORDER_REFIT = 98,
CMD_CLONE_ORDER = 99,
CMD_CLEAR_AREA = 100,
CMD_CLONE_ORDER = 99,
CMD_CLEAR_AREA = 100,
CMD_MONEY_CHEAT = 102,
CMD_BUILD_CANAL = 103,
CMD_MONEY_CHEAT = 102,
CMD_BUILD_CANAL = 103,
CMD_PLAYER_CTRL = 104, // used in multiplayer to create a new player etc.
CMD_LEVEL_LAND = 105, // level land
CMD_PLAYER_CTRL = 104, // used in multiplayer to create a new player etc.
CMD_LEVEL_LAND = 105, // level land
CMD_REFIT_RAIL_VEHICLE = 106,
CMD_RESTORE_ORDER_INDEX = 107,
CMD_BUILD_LOCK = 108,
CMD_REFIT_RAIL_VEHICLE = 106,
CMD_RESTORE_ORDER_INDEX = 107,
CMD_BUILD_LOCK = 108,
CMD_BUILD_SIGNAL_TRACK = 110,
CMD_REMOVE_SIGNAL_TRACK = 111,
CMD_BUILD_SIGNAL_TRACK = 110,
CMD_REMOVE_SIGNAL_TRACK = 111,
CMD_GIVE_MONEY = 113,
CMD_CHANGE_PATCH_SETTING = 114,
CMD_GIVE_MONEY = 113,
CMD_CHANGE_PATCH_SETTING = 114,
CMD_SET_AUTOREPLACE = 115,
CMD_REPLACE_VEHICLE = 115,
CMD_CLONE_VEHICLE = 116,
CMD_CLONE_VEHICLE = 116,
CMD_MASS_START_STOP = 117,
CMD_DEPOT_SELL_ALL_VEHICLES = 118,
CMD_DEPOT_MASS_AUTOREPLACE = 119,
};
enum {
DC_EXEC = 0x01,
DC_AUTO = 0x02, // don't allow building on structures
DC_QUERY_COST = 0x04, // query cost only, don't build.
DC_NO_WATER = 0x08, // don't allow building on water
DC_NO_RAIL_OVERLAP = 0x10, // don't allow overlap of rails (used in buildrail)
DC_AI_BUILDING = 0x20, // special building rules for AI
DC_NO_TOWN_RATING = 0x40, // town rating does not disallow you from building
DC_FORCETEST = 0x80, // force test too.
DC_EXEC = 1,
DC_AUTO = 2, // don't allow building on structures
DC_QUERY_COST = 4, // query cost only, don't build.
DC_NO_WATER = 8, // don't allow building on water
DC_NO_RAIL_OVERLAP = 0x10, // don't allow overlap of rails (used in buildrail)
DC_AI_BUILDING = 0x20, // special building rules for AI
DC_NO_TOWN_RATING = 0x40, // town rating does not disallow you from building
DC_FORCETEST = 0x80, // force test too.
CMD_ERROR = ((int32)0x80000000),
};
@@ -158,11 +154,11 @@ enum {
#define CMD_MSG(x) ((x)<<16)
enum {
CMD_AUTO = 0x0200,
CMD_NO_WATER = 0x0400,
CMD_NETWORK_COMMAND = 0x0800, // execute the command without sending it on the network
CMD_AUTO = 0x200,
CMD_NO_WATER = 0x400,
CMD_NETWORK_COMMAND = 0x800, // execute the command without sending it on the network
CMD_NO_TEST_IF_IN_NETWORK = 0x1000, // When enabled, the command will bypass the no-DC_EXEC round if in network
CMD_SHOW_NO_ERROR = 0x2000,
CMD_SHOW_NO_ERROR = 0x2000,
};
/** Command flags for the command table
@@ -173,8 +169,6 @@ enum {
CMD_OFFLINE = 0x2, /// the command cannot be executed in a multiplayer game; single-player only
};
typedef int32 CommandProc(TileIndex tile, uint32 flags, uint32 p1, uint32 p2);
typedef struct Command {
CommandProc *proc;
byte flags;
@@ -195,14 +189,8 @@ static inline bool CmdFailed(int32 res)
}
/* command.c */
typedef void CommandCallback(bool success, TileIndex tile, uint32 p1, uint32 p2);
int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd);
#ifdef ENABLE_NETWORK
void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback);
#endif /* ENABLE_NETWORK */
int32 DoCommand(int x, int y, uint32 p1, uint32 p2, uint32 flags, uint procc);
int32 DoCommandByTile(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
extern const char* _cmd_text; // Text, which gets sent with a command

135
configure vendored
View File

@@ -3,52 +3,44 @@
# This 'configure' script is a very easy wrapper around 'make updateconf'
# It allows cross-compilers to do their job much more easy.
showhelp() {
function showhelp() {
echo "Configure for OpenTTD"
echo ""
echo "Usage:"
echo " $0 --your_options"
echo ""
echo "Params:"
echo " --debug Create debug-release [no]"
echo " --profile Create profile-release [no]"
echo " --dedicated Make a dedicated build [no]"
echo " --revision Set the revision of the compilation [detected]"
echo " --target-cc Sets the target-compiler [\$CC]"
echo " --target-cxx Sets the C++ target-compiler []"
echo " --host-cc Sets the host-compiler [\$CC]"
echo " --host-cxx Sets the C++ host-compiler []"
echo " --os Sets the OS. Listens to: [detected]"
echo " UNIX, OSX, FREEBSD, MORPHOS"
echo " BEOS, SUNOS, CYGWIN, MINGW, OS2"
echo " --windres Sets the windres (Windows) [windres]"
echo " --force-le Force LE platform [no]"
echo " --force-be Force BE platform [no]"
echo " --debug Create debug-release [no]"
echo " --profile Create profile-release [no]"
echo " --dedicated Make a dedicated build [no]"
echo " --revision Set the revision of the compilation [detected]"
echo " --target-cc Sets the target-compiler [\$CC]"
echo " --target-cxx Sets the C++ target-compiler []"
echo " --host-cc Sets the host-compiler [\$CC]"
echo " --os Sets the OS. Listens to: [detected]"
echo " UNIX, OSX, FREEBSD, MORPHOS"
echo " BEOS, SUNOS, CYGWIN, MINGW"
echo " --windres Sets the windres (Windows) [windres]"
echo " --force-le Force LE platform [no]"
echo " --force-be Force BE platform [no]"
echo ""
echo "Params that can be used with --with or --without"
echo " (e.g.: --without-static disables static (default))"
echo " static Do you want a static build? [no]"
echo " directmusic Do you want direct-music? [no]"
echo " zlib Do you want zlib-support? [yes]"
echo " sdl Do you want SDL-support? [yes]"
echo " png Do you want PNG-support? [yes]"
echo " iconv Do you want iconv-support? [no]"
echo " network Do you want network-support? [yes]"
echo " cocoa Do you want cocoa-support? (MacOSX) [no]"
echo " freetype Do you want freetype-support? [yes]"
echo " fontconfig Do you want fontconfig-support? [yes]"
echo " static Do you want a static build? [no]"
echo " directmusic Do you want direct-music? [no]"
echo " zlib Do you want zlib-support? [yes]"
echo " sdl Do you want SDL-support? [yes]"
echo " png Do you want PNG-support? [yes]"
echo " cocoa Do you want cocoa-support? (MacOSX) [no]"
echo ""
echo "Params used to configure external libs:"
echo " --static-zlib-path Set the path to your static zlib []"
echo " --sdl-config Where is your sdl-config [sdl-config]"
echo " --libpng-config Where is your libpng-config [libpng-config]"
echo " --freetype-config Where is your freetype-config [freetype-config]"
echo " --fontconfig-config Where is your fontconfig-config [pkg-config fontconfig]"
echo " --with-iconv Set the path to your iconv headers []"
echo " --static-zlib-path Set the path to your static zlib []"
echo " --sdl-config Where is your sdl-config [sdl-config]"
echo " --libpng-config Where is your libpng-config [libpng-config]"
echo " "
}
handle() {
function handle() {
PARAM="$PARAM \"$1=`awk 'BEGIN { FS="="; $0="'"$2"'"; print $2;}'`\""
}
@@ -91,23 +83,17 @@ do
ITEM="CC_TARGET"
;;
--target-cxx=*)
handle "CXX_TARGET" "$n"
TARGET_CXX=`awk 'BEGIN { FS="="; $0="'"$n"'"; print $2;}'`
;;
--target-cxx)
SITEM="CXX_TARGET"
SITEM="TARGET_CXX"
;;
--host-cc=*)
handle "CC_HOST" "$n"
handle CC_HOST "$n"
;;
--host-cc)
ITEM="CC_HOST"
;;
--host-cxx=*)
handle "CXX_HOST" "$n"
;;
--host-cxx)
ITEM="CXX_HOST"
;;
--host-cflags=*)
handle CFLAGS_HOST "$n"
;;
@@ -163,40 +149,12 @@ do
--without-png)
PARAM="$PARAM WITH_PNG="
;;
--with-iconv)
PARAM="$PARAM WITH_ICONV=1"
;;
--with-iconv=*)
PARAM="$PARAM WITH_ICONV=1"
handle WITH_ICONV_PATH "$n"
;;
--without-iconv)
PARAM="$PARAM WITH_ICONV="
;;
--with-cocoa)
PARAM="$PARAM WITH_COCOA=1"
;;
--with-network)
PARAM="$PARAM WITH_NETWORK=1"
;;
--without-network)
PARAM="$PARAM WITH_NETWORK="
;;
--without-cocoa)
PARAM="$PARAM WITH_COCOA="
;;
--with-freetype)
PARAM="$PARAM WITH_FREETYPE=1"
;;
--without-freetype)
PARAM="$PARAM WITH_FREETYPE="
;;
--with-fontconfig)
PARAM="$PARAM WITH_FONTCONFIG=1"
;;
--without-fontconfig)
PARAM="$PARAM WITH_FONTCONFIG="
;;
--static-zlib-path=*)
handle STATIC_ZLIB_PATH "$n"
;;
@@ -212,21 +170,9 @@ do
--libpng-config=*)
handle LIBPNG_CONFIG "$n"
;;
--libpng-config)
--lib-png-config)
ITEM="LIBPNG_CONFIG"
;;
--freetype-config=*)
handle FREETYPE_CONFIG "$n"
;;
--freetype-config)
ITEM="FREETYPE_CONFIG"
;;
--fontconfig-config=*)
handle FONTCONFIG_CONFIG "$n"
;;
--fontconfig-config)
ITEM="FONTCONFIG_CONFIG"
;;
--*=*)
echo -n "Unknown switch "
@@ -277,9 +223,6 @@ then
BEOS)
PARAM="$PARAM BEOS=1 UNIX=1"
;;
OS2)
PARAM="$PARAM OS2=1 UNIX=1"
;;
SUNOS)
PARAM="$PARAM SUNOS=1 UNIX=1"
;;
@@ -307,14 +250,24 @@ then
fi
# First remove the Makefile.config, else you can have double entries
rm -f Makefile.config
if test -e "Makefile.config"
then
rm -f Makefile.config
fi
echo "make upgradeconf $PARAM" > ./Makefile.run
. ./Makefile.run
rm -f ./Makefile.run
echo "make upgradeconf $PARAM" > Makefile.run
. Makefile.run
rm -f Makefile.run
# Makefile.config currently doesn't support custom RELEASE (revision), so, we add the line
# yourself!
# Makefile.config currently doesn't support custom CXX, so, we add the line
# ourself!
if ! test -z "$TARGET_CXX"
then
echo "CXX=$TARGET_CXX" >> Makefile.config
fi
# Same for RELEASE (read: REVISION)
if ! test -z "$RELEASE"
then

275
console.c
View File

@@ -26,9 +26,12 @@
#define ICON_TOKEN_COUNT 20
// ** main console ** //
static Window *_iconsole_win; // Pointer to console window
static bool _iconsole_inited;
static char *_iconsole_buffer[ICON_BUFFER + 1];
static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
static Textbuf _iconsole_cmdline;
static byte _iconsole_scroll;
// ** stdlib ** //
byte _stdlib_developer = 1;
@@ -50,29 +53,27 @@ static void IConsoleClearCommand(void)
_iconsole_cmdline.width = 0;
_iconsole_cmdline.caretpos = 0;
_iconsole_cmdline.caretxoffs = 0;
SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
SetWindowDirty(_iconsole_win);
}
static inline void IConsoleResetHistoryPos(void) {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
static void IConsoleHistoryAdd(const char *cmd);
static void IConsoleHistoryAdd(const char* cmd);
static void IConsoleHistoryNavigate(int direction);
// ** console window ** //
static void IConsoleWndProc(Window *w, WindowEvent *e)
static void IConsoleWndProc(Window* w, WindowEvent* e)
{
static byte iconsole_scroll = ICON_BUFFER;
switch (e->event) {
case WE_PAINT: {
int i = iconsole_scroll;
int i = _iconsole_scroll;
int max = (w->height / ICON_LINE_HEIGHT) - 1;
int delta = 0;
GfxFillRect(w->left, w->top, w->width, w->height - 1, 0);
while ((i > 0) && (i > iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
while ((i > 0) && (i > _iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
DoDrawString(_iconsole_buffer[i], 5,
w->height - (iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
w->height - (_iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
i--;
}
/* If the text is longer than the window, don't show the starting ']' */
@@ -93,11 +94,12 @@ static void IConsoleWndProc(Window *w, WindowEvent *e)
SetWindowDirty(w);
break;
case WE_DESTROY:
_iconsole_win = NULL;
_iconsole_mode = ICONSOLE_CLOSED;
break;
case WE_KEYPRESS:
e->we.keypress.cont = false;
switch (e->we.keypress.keycode) {
e->keypress.cont = false;
switch (e->keypress.keycode) {
case WKC_UP:
IConsoleHistoryNavigate(+1);
SetWindowDirty(w);
@@ -107,35 +109,31 @@ static void IConsoleWndProc(Window *w, WindowEvent *e)
SetWindowDirty(w);
break;
case WKC_SHIFT | WKC_PAGEUP:
if (iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) {
iconsole_scroll = 0;
} else {
iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1;
}
if (_iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0)
_iconsole_scroll = 0;
else
_iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1;
SetWindowDirty(w);
break;
case WKC_SHIFT | WKC_PAGEDOWN:
if (iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) {
iconsole_scroll = ICON_BUFFER;
} else {
iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1;
}
if (_iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER)
_iconsole_scroll = ICON_BUFFER;
else
_iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1;
SetWindowDirty(w);
break;
case WKC_SHIFT | WKC_UP:
if (iconsole_scroll <= 0) {
iconsole_scroll = 0;
} else {
--iconsole_scroll;
}
if (_iconsole_scroll <= 0)
_iconsole_scroll = 0;
else
--_iconsole_scroll;
SetWindowDirty(w);
break;
case WKC_SHIFT | WKC_DOWN:
if (iconsole_scroll >= ICON_BUFFER) {
iconsole_scroll = ICON_BUFFER;
} else {
++iconsole_scroll;
}
if (_iconsole_scroll >= ICON_BUFFER)
_iconsole_scroll = ICON_BUFFER;
else
++_iconsole_scroll;
SetWindowDirty(w);
break;
case WKC_BACKQUOTE:
@@ -150,7 +148,7 @@ static void IConsoleWndProc(Window *w, WindowEvent *e)
break;
case WKC_CTRL | WKC_RETURN:
_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
IConsoleResize(w);
IConsoleResize();
MarkWholeScreenDirty();
break;
case (WKC_CTRL | 'V'):
@@ -167,26 +165,25 @@ static void IConsoleWndProc(Window *w, WindowEvent *e)
SetWindowDirty(w);
break;
case WKC_BACKSPACE: case WKC_DELETE:
if (DeleteTextBufferChar(&_iconsole_cmdline, e->we.keypress.keycode)) {
if (DeleteTextBufferChar(&_iconsole_cmdline, e->keypress.keycode)) {
IConsoleResetHistoryPos();
SetWindowDirty(w);
}
break;
case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
if (MoveTextBufferPos(&_iconsole_cmdline, e->we.keypress.keycode)) {
if (MoveTextBufferPos(&_iconsole_cmdline, e->keypress.keycode)) {
IConsoleResetHistoryPos();
SetWindowDirty(w);
}
break;
default:
if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) {
iconsole_scroll = ICON_BUFFER;
InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key);
if (IsValidAsciiChar(e->keypress.ascii)) {
_iconsole_scroll = ICON_BUFFER;
InsertTextBufferChar(&_iconsole_cmdline, e->keypress.ascii);
IConsoleResetHistoryPos();
SetWindowDirty(w);
} else {
e->we.keypress.cont = true;
}
} else
e->keypress.cont = true;
break;
}
}
@@ -213,8 +210,11 @@ void IConsoleInit(void)
_icolour_warn = 13;
_icolour_dbg = 5;
_icolour_cmd = 2;
_iconsole_scroll = ICON_BUFFER;
_iconsole_historypos = ICON_HISTORY_SIZE - 1;
_iconsole_inited = true;
_iconsole_mode = ICONSOLE_CLOSED;
_iconsole_win = NULL;
#ifdef ENABLE_NETWORK /* Initialize network only variables */
_redirect_console_to_client = 0;
@@ -250,7 +250,7 @@ static void IConsoleClear(void)
IConsoleClearBuffer();
}
static void IConsoleWriteToLogFile(const char *string)
static void IConsoleWriteToLogFile(const char* string)
{
if (_iconsole_output_file != NULL) {
// if there is an console output file ... also print it there
@@ -273,22 +273,25 @@ bool CloseConsoleLogIfActive(void)
void IConsoleFree(void)
{
_iconsole_inited = false;
IConsoleClear();
CloseConsoleLogIfActive();
}
void IConsoleResize(Window *w)
void IConsoleResize(void)
{
_iconsole_win = FindWindowById(WC_CONSOLE, 0);
switch (_iconsole_mode) {
case ICONSOLE_OPENED:
w->height = _screen.height / 3;
w->width = _screen.width;
_iconsole_win->height = _screen.height / 3;
_iconsole_win->width = _screen.width;
break;
case ICONSOLE_FULL:
w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
w->width = _screen.width;
_iconsole_win->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
_iconsole_win->width = _screen.width;
break;
default: return;
default: break;
}
MarkWholeScreenDirty();
@@ -297,15 +300,16 @@ void IConsoleResize(Window *w)
void IConsoleSwitch(void)
{
switch (_iconsole_mode) {
case ICONSOLE_CLOSED: {
Window *w = AllocateWindowDesc(&_iconsole_window_desc);
w->height = _screen.height / 3;
w->width = _screen.width;
case ICONSOLE_CLOSED:
_iconsole_win = AllocateWindowDesc(&_iconsole_window_desc);
_iconsole_win->height = _screen.height / 3;
_iconsole_win->width = _screen.width;
_iconsole_mode = ICONSOLE_OPENED;
SETBIT(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll
} break;
break;
case ICONSOLE_OPENED: case ICONSOLE_FULL:
DeleteWindowById(WC_CONSOLE, 0);
_iconsole_win = NULL;
_iconsole_mode = ICONSOLE_CLOSED;
CLRBIT(_no_scroll, SCROLL_CON);
break;
@@ -322,7 +326,7 @@ void IConsoleOpen(void) {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch(
* scroll, etc. Put it to the beginning as it is the latest text
* @param cmd Text to be entered into the 'history'
*/
static void IConsoleHistoryAdd(const char *cmd)
static void IConsoleHistoryAdd(const char* cmd)
{
free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
@@ -367,9 +371,8 @@ static void IConsoleHistoryNavigate(int direction)
* @param color_code the colour of the command. Red in case of errors, etc.
* @param string the message entered or output on the console (notice, error, etc.)
*/
void IConsolePrint(uint16 color_code, const char *string)
void IConsolePrint(uint16 color_code, const char* string)
{
char *str;
#ifdef ENABLE_NETWORK
if (_redirect_console_to_client != 0) {
/* Redirect the string to the client */
@@ -378,31 +381,32 @@ void IConsolePrint(uint16 color_code, const char *string)
}
#endif
/* Create a copy of the string, strip if of colours and invalid
* characters and (when applicable) assign it to the console buffer */
str = strdup(string);
str_strip_colours(str);
str_validate(str);
if (_network_dedicated) {
printf("%s\n", str);
IConsoleWriteToLogFile(str);
free(str); // free duplicated string since it's not used anymore
printf("%s\n", string);
IConsoleWriteToLogFile(string);
return;
}
if (!_iconsole_inited) return;
/* move up all the strings in the buffer one place and do the same for colour
* to accomodate for the new command/message */
free(_iconsole_buffer[0]);
memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER);
_iconsole_buffer[ICON_BUFFER] = str;
_iconsole_buffer[ICON_BUFFER] = strdup(string);
{ // filter out unprintable characters
char *i;
for (i = _iconsole_buffer[ICON_BUFFER]; *i != '\0'; i++)
if (!IsValidAsciiChar((byte)*i)) *i = ' ';
}
memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER);
_iconsole_cbuffer[ICON_BUFFER] = color_code;
IConsoleWriteToLogFile(_iconsole_buffer[ICON_BUFFER]);
IConsoleWriteToLogFile(string);
SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
if (_iconsole_win != NULL) SetWindowDirty(_iconsole_win);
}
/**
@@ -428,7 +432,7 @@ void CDECL IConsolePrintF(uint16 color_code, const char *s, ...)
* @debug() in debug.c. You need at least a level 2 (developer) for debugging
* messages to show up
*/
void IConsoleDebug(const char *string)
void IConsoleDebug(const char* string)
{
if (_stdlib_developer > 1)
IConsolePrintF(_icolour_dbg, "dbg: %s", string);
@@ -439,7 +443,7 @@ void IConsoleDebug(const char *string)
* errors or mishaps, but non-fatal. You need at least a level 1 (developer) for
* debugging messages to show up
*/
void IConsoleWarning(const char *string)
void IConsoleWarning(const char* string)
{
if (_stdlib_developer > 0)
IConsolePrintF(_icolour_warn, "WARNING: %s", string);
@@ -449,7 +453,7 @@ void IConsoleWarning(const char *string)
* It is possible to print error information to the console. This can include
* game errors, or errors in general you would want the user to notice
*/
void IConsoleError(const char *string)
void IConsoleError(const char* string)
{
IConsolePrintF(_icolour_err, "ERROR: %s", string);
}
@@ -565,41 +569,40 @@ void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *
* three types, just with different variables. Yes, templates would be handy. It was
* either this define or an even more ugly void* magic function
*/
#define IConsoleAddSorted(_base, item_new, IConsoleType, type) \
{ \
IConsoleType *item, *item_before; \
/* first command */ \
if (_base == NULL) { \
_base = item_new; \
return; \
} \
\
item_before = NULL; \
item = _base; \
\
/* BEGIN - Alphabetically insert the commands into the linked list */ \
while (item != NULL) { \
int i = strcmp(item->name, item_new->name); \
if (i == 0) { \
IConsoleError(type " with this name already exists; insertion aborted"); \
free(item_new); \
return; \
} \
\
if (i > 0) break; /* insert at this position */ \
\
item_before = item; \
item = item->next; \
} \
\
if (item_before == NULL) { \
_base = item_new; \
} else { \
item_before->next = item_new; \
} \
\
item_new->next = item; \
/* END - Alphabetical insert */ \
#define IConsoleAddSorted(_base, item_new, IConsoleType, type) \
{ \
IConsoleType *item, *item_before; \
/* first command */ \
if (_base == NULL) { \
_base = item_new; \
return; \
} \
\
item_before = NULL; \
item = _base; \
\
/* BEGIN - Alphabetically insert the commands into the linked list */ \
while (item != NULL) { \
int i = strcmp(item->name, item_new->name); \
if (i == 0) { \
IConsoleError(type " with this name already exists; insertion aborted"); \
free(item_new); \
return; \
} \
\
if (i > 0) break; /* insert at this position */ \
\
item_before = item; \
item = item->next; \
} \
\
if (item_before == NULL) { \
_base = item_new; \
} else \
item_before->next = item_new; \
\
item_new->next = item; \
/* END - Alphabetical insert */ \
}
/**
@@ -675,7 +678,7 @@ IConsoleAlias *IConsoleAliasGet(const char *name)
/** copy in an argument into the aliasstream */
static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
{
int len = min(ICON_MAX_STREAMSIZE - bufpos, (uint)strlen(src));
int len = min(ICON_MAX_STREAMSIZE - bufpos, strlen(src));
strncpy(dst, src, len);
return len;
@@ -688,11 +691,11 @@ static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
* @param tokencount the number of parameters passed
* @param *tokens are the parameters given to the original command (0 is the first param)
*/
static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
static void IConsoleAliasExec(const IConsoleAlias* alias, byte tokencount, char* tokens[ICON_TOKEN_COUNT])
{
const char *cmdptr;
char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
uint i;
int i;
uint a_index, astream_i;
memset(&aliases, 0, sizeof(aliases));
@@ -755,7 +758,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
}
}
for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
for (i = 0; i <= (int)a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
}
/**
@@ -854,12 +857,12 @@ static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value)
* @param *var the variable in question
* @param *value the new value
*/
static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value)
static void IConsoleVarSetStringvalue(const IConsoleVar *var, char *value)
{
if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return;
IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
ttd_strlcpy(var->addr, value, var->size);
ttd_strlcpy((char*)var->addr, (char*)value, var->size);
IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
IConsoleVarPrintSetValue(var); // print out the new value, giving feedback
return;
@@ -1051,7 +1054,7 @@ void IConsoleCmdExec(const char *cmdstr)
if (cmdstr[0] == '#') return; // comments
for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
if (!IsValidAsciiChar(*cmdptr)) {
IConsoleError("command contains malformed characters, aborting");
IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr);
return;
@@ -1086,12 +1089,6 @@ void IConsoleCmdExec(const char *cmdstr)
case '"': /* Tokens enclosed in "" are one token */
longtoken = !longtoken;
break;
case '\\': /* Escape character for "" */
if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
tokenstream[tstream_i++] = *++cmdptr;
break;
}
/* fallthrough */
default: /* Normal character */
tokenstream[tstream_i++] = *cmdptr;
@@ -1105,10 +1102,8 @@ void IConsoleCmdExec(const char *cmdstr)
if (_stdlib_con_developer) {
uint i;
for (i = 0; tokens[i] != NULL; i++) {
for (i = 0; tokens[i] != NULL; i++)
IConsolePrintF(_icolour_dbg, "condbg: token %d is: '%s'", i, tokens[i]);
}
}
if (tokens[0] == '\0') return; // don't execute empty commands
@@ -1116,33 +1111,31 @@ void IConsoleCmdExec(const char *cmdstr)
* First try commands, then aliases, and finally variables. Execute
* the found action taking into account its hooking code
*/
cmd = IConsoleCmdGet(tokens[0]);
if (cmd != NULL) {
cmd = IConsoleCmdGet(tokens[0]);
if (cmd != NULL) {
if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) {
IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION);
if (cmd->proc(t_index, tokens)) { // index started with 0
IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
} else {
cmd->proc(0, NULL); // if command failed, give help
}
} else cmd->proc(0, NULL); // if command failed, give help
}
return;
}
return;
}
t_index--; // ignore the variable-name for comfort for both aliases and variaables
alias = IConsoleAliasGet(tokens[0]);
if (alias != NULL) {
IConsoleAliasExec(alias, t_index, &tokens[1]);
return;
}
t_index--; // ignore the variable-name for comfort for both aliases and variaables
alias = IConsoleAliasGet(tokens[0]);
if (alias != NULL) {
IConsoleAliasExec(alias, t_index, &tokens[1]);
return;
}
var = IConsoleVarGet(tokens[0]);
if (var != NULL) {
if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) {
IConsoleVarExec(var, t_index, &tokens[1]);
}
return;
}
var = IConsoleVarGet(tokens[0]);
if (var != NULL) {
if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS))
IConsoleVarExec(var, t_index, &tokens[1]);
IConsoleError("command or variable not found");
return;
}
IConsoleError("command or variable not found");
}

View File

@@ -101,12 +101,12 @@ typedef struct IConsoleAlias {
char *cmdline; // command(s) that is/are being aliased
} IConsoleAlias;
/* console parser */
VARDEF IConsoleCmd *_iconsole_cmds; // list of registred commands
VARDEF IConsoleVar *_iconsole_vars; // list of registred vars
VARDEF IConsoleAlias *_iconsole_aliases; // list of registred aliases
// ** console parser ** //
IConsoleCmd *_iconsole_cmds; // list of registred commands
IConsoleVar *_iconsole_vars; // list of registred vars
IConsoleAlias *_iconsole_aliases; // list of registred aliases
/* console colors/modes */
// ** console colors/modes ** //
VARDEF byte _icolour_def;
VARDEF byte _icolour_err;
VARDEF byte _icolour_warn;
@@ -114,47 +114,47 @@ VARDEF byte _icolour_dbg;
VARDEF byte _icolour_cmd;
VARDEF IConsoleModes _iconsole_mode;
/* console functions */
// ** console functions ** //
void IConsoleInit(void);
void IConsoleFree(void);
void IConsoleClearBuffer(void);
void IConsoleResize(Window *w);
void IConsoleResize(void);
void IConsoleSwitch(void);
void IConsoleClose(void);
void IConsoleOpen(void);
/* console output */
// ** console output ** //
void IConsolePrint(uint16 color_code, const char *string);
void CDECL IConsolePrintF(uint16 color_code, const char *s, ...);
void IConsoleDebug(const char *string);
void IConsoleWarning(const char *string);
void IConsoleError(const char *string);
/* Commands */
// *** Commands *** //
void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc);
void IConsoleAliasRegister(const char *name, const char *cmd);
IConsoleCmd *IConsoleCmdGet(const char *name);
IConsoleAlias *IConsoleAliasGet(const char *name);
/* Variables */
// *** Variables *** //
void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help);
void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help);
IConsoleVar* IConsoleVarGet(const char *name);
void IConsoleVarPrintGetValue(const IConsoleVar *var);
void IConsoleVarPrintSetValue(const IConsoleVar *var);
/* Parser */
// *** Parser *** //
void IConsoleCmdExec(const char *cmdstr);
void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[]);
/* console std lib (register ingame commands/aliases/variables) */
// ** console std lib (register ingame commands/aliases/variables) ** //
void IConsoleStdLibRegister(void);
/* Hooking code */
// ** Hooking code ** //
void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc);
void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc);
void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc);
/* Supporting functions */
// ** Supporting functions **//
bool GetArgumentInteger(uint32 *value, const char *arg);
#endif /* CONSOLE_H */

View File

@@ -15,14 +15,8 @@
#include "network_udp.h"
#include "command.h"
#include "settings.h"
#include "fios.h"
#include "hal.h" /* for file list */
#include "vehicle.h"
#include "station.h"
#include "strings.h"
#include "screenshot.h"
#include "genworld.h"
#include "date.h"
#include "network.h"
// ** scriptfile handling ** //
static FILE *_script_file;
@@ -137,13 +131,15 @@ DEF_CONSOLE_CMD(ConStopAllVehicles)
}
FOR_ALL_VEHICLES(v) {
/* Code ripped from CmdStartStopTrain. Can't call it, because of
* ownership problems, so we'll duplicate some code, for now */
if (v->type == VEH_Train)
v->u.rail.days_since_order_progr = 0;
v->vehstatus |= VS_STOPPED;
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
if (IsValidVehicle(v)) {
/* Code ripped from CmdStartStopTrain. Can't call it, because of
* ownership problems, so we'll duplicate some code, for now */
if (v->type == VEH_Train)
v->u.rail.days_since_order_progr = 0;
v->vehstatus |= VS_STOPPED;
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
}
}
return true;
}
@@ -160,10 +156,6 @@ DEF_CONSOLE_CMD(ConScrollToTile)
if (argc == 2) {
uint32 result;
if (GetArgumentInteger(&result, argv[1])) {
if (result >= MapSize()) {
IConsolePrint(_icolour_err, "Tile does not exist");
return true;
}
ScrollMainWindowToTile((TileIndex)result);
return true;
}
@@ -187,14 +179,13 @@ DEF_CONSOLE_CMD(ConSave)
if (argc == 2) {
char buf[200];
snprintf(buf, lengthof(buf), "%s%s%s.sav", _paths.save_dir, PATHSEP, argv[1]);
snprintf(buf, lengthof(buf), "%s%s%s.sav", _path.save_dir, PATHSEP, argv[1]);
IConsolePrint(_icolour_def, "Saving map...");
if (SaveOrLoad(buf, SL_SAVE) != SL_OK) {
IConsolePrint(_icolour_err, "SaveMap failed");
} else {
} else
IConsolePrintF(_icolour_def, "Map sucessfully saved to %s", buf);
}
return true;
}
@@ -249,9 +240,8 @@ DEF_CONSOLE_CMD(ConLoad)
} break;
default: IConsolePrintF(_icolour_err, "%s: Not a savegame.", file);
}
} else {
} else
IConsolePrintF(_icolour_err, "%s: No such file or directory.", file);
}
FiosFreeSavegameList();
return true;
@@ -275,9 +265,8 @@ DEF_CONSOLE_CMD(ConRemove)
if (item != NULL) {
if (!FiosDelete(item->name))
IConsolePrintF(_icolour_err, "%s: Failed to delete file", file);
} else {
} else
IConsolePrintF(_icolour_err, "%s: No such file or directory.", file);
}
FiosFreeSavegameList();
return true;
@@ -327,9 +316,8 @@ DEF_CONSOLE_CMD(ConChangeDirectory)
break;
default: IConsolePrintF(_icolour_err, "%s: Not a directory.", file);
}
} else {
} else
IConsolePrintF(_icolour_err, "%s: No such file or directory.", file);
}
FiosFreeSavegameList();
return true;
@@ -345,7 +333,7 @@ DEF_CONSOLE_CMD(ConPrintWorkingDirectory)
}
// XXX - Workaround for broken file handling
FiosGetSavegameList(SLD_LOAD_GAME);
FiosGetSavegameList(&_fios_num, SLD_LOAD_GAME);
FiosFreeSavegameList();
FiosGetDescText(&path, NULL);
@@ -413,9 +401,8 @@ DEF_CONSOLE_CMD(ConBan)
banip = inet_ntoa(*(struct in_addr *)&ci->client_ip);
SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED);
IConsolePrint(_icolour_def, "Client banned");
} else {
} else
IConsolePrint(_icolour_def, "Client not online, banned IP");
}
/* Add user to ban-list */
for (index = 0; index < lengthof(_network_ban_list); index++) {
@@ -487,9 +474,8 @@ DEF_CONSOLE_CMD(ConPauseGame)
if (_pause == 0) {
DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
IConsolePrint(_icolour_def, "Game paused.");
} else {
} else
IConsolePrint(_icolour_def, "Game is already paused.");
}
return true;
}
@@ -504,9 +490,8 @@ DEF_CONSOLE_CMD(ConUnPauseGame)
if (_pause != 0) {
DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
IConsolePrint(_icolour_def, "Game unpaused.");
} else {
} else
IConsolePrint(_icolour_def, "Game is already unpaused.");
}
return true;
}
@@ -527,17 +512,8 @@ DEF_CONSOLE_CMD(ConRcon)
DEF_CONSOLE_CMD(ConStatus)
{
static const char* const stat_str[] = {
"inactive",
"authorizing",
"authorized",
"waiting",
"loading map",
"map done",
"ready",
"active"
};
static const char *stat_str[] = {"inactive", "authorized", "waiting", "loading map", "map done", "ready", "active"};
const char *status;
const NetworkClientState *cs;
if (argc == 0) {
@@ -548,13 +524,10 @@ DEF_CONSOLE_CMD(ConStatus)
FOR_ALL_CLIENTS(cs) {
int lag = NetworkCalculateLag(cs);
const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
const char* status;
status = (cs->status < lengthof(stat_str) ? stat_str[cs->status] : "unknown");
status = (cs->status <= STATUS_ACTIVE) ? stat_str[cs->status] : "unknown";
IConsolePrintF(8, "Client #%1d name: '%s' status: '%s' frame-lag: %3d company: %1d IP: %s unique-id: '%s'",
cs->index, ci->client_name, status, lag,
ci->client_playas + (IsValidPlayer(ci->client_playas) ? 1 : 0),
GetPlayerIP(ci), ci->unique_id);
cs->index, ci->client_name, status, lag, ci->client_playas, GetPlayerIP(ci), ci->unique_id);
}
return true;
@@ -578,8 +551,7 @@ DEF_CONSOLE_CMD(ConServerInfo)
return true;
}
DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount)
{
DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount) {
/* XXX - hardcoded, string limiation -- TrueLight
* XXX - also see network.c:NetworkStartup ~1356 */
if (_network_game_info.clients_max > 10) {
@@ -590,8 +562,7 @@ DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount)
return true;
}
DEF_CONSOLE_HOOK(ConHookValidateMaxCompaniesCount)
{
DEF_CONSOLE_HOOK(ConHookValidateMaxCompaniesCount) {
if (_network_game_info.companies_max > MAX_PLAYERS) {
_network_game_info.companies_max = MAX_PLAYERS;
IConsoleError("Maximum companies out of bounds, truncating to limit.");
@@ -600,8 +571,7 @@ DEF_CONSOLE_HOOK(ConHookValidateMaxCompaniesCount)
return true;
}
DEF_CONSOLE_HOOK(ConHookValidateMaxSpectatorsCount)
{
DEF_CONSOLE_HOOK(ConHookValidateMaxSpectatorsCount) {
/* XXX @see ConHookValidateMaxClientsCount */
if (_network_game_info.spectators_max > 10) {
_network_game_info.spectators_max = 10;
@@ -611,12 +581,6 @@ DEF_CONSOLE_HOOK(ConHookValidateMaxSpectatorsCount)
return true;
}
DEF_CONSOLE_HOOK(ConHookCheckMinPlayers)
{
CheckMinPlayers();
return true;
}
DEF_CONSOLE_CMD(ConKick)
{
NetworkClientInfo *ci;
@@ -650,19 +614,18 @@ DEF_CONSOLE_CMD(ConKick)
if (ci != NULL) {
SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED);
} else {
} else
IConsoleError("Client not found");
}
return true;
}
DEF_CONSOLE_CMD(ConResetCompany)
{
const Player *p;
const NetworkClientState *cs;
const NetworkClientInfo *ci;
PlayerID index;
Player *p;
NetworkClientState *cs;
NetworkClientInfo *ci;
byte index;
if (argc == 0) {
IConsoleHelp("Remove an idle company from the game. Usage: 'reset_company <company-id>'");
@@ -672,15 +635,16 @@ DEF_CONSOLE_CMD(ConResetCompany)
if (argc != 2) return false;
index = atoi(argv[1]) - 1;
index = atoi(argv[1]);
/* Check valid range */
if (!IsValidPlayer(index)) {
if (index < 1 || index > MAX_PLAYERS) {
IConsolePrintF(_icolour_err, "Company does not exist. Company-id must be between 1 and %d.", MAX_PLAYERS);
return true;
}
/* Check if company does exist */
index--;
p = GetPlayer(index);
if (!p->is_active) {
IConsoleError("Company does not exist.");
@@ -695,13 +659,13 @@ DEF_CONSOLE_CMD(ConResetCompany)
/* Check if the company has active players */
FOR_ALL_CLIENTS(cs) {
ci = DEREF_CLIENT_INFO(cs);
if (ci->client_playas == index) {
if (ci->client_playas - 1 == index) {
IConsoleError("Cannot remove company: a client is connected to that company.");
return true;
}
}
ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
if (ci->client_playas == index) {
if (ci->client_playas - 1 == index) {
IConsoleError("Cannot remove company: the server is connected to that company.");
return true;
}
@@ -722,11 +686,11 @@ DEF_CONSOLE_CMD(ConNetworkClients)
return true;
}
FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
IConsolePrintF(8, "Client #%1d name: '%s' company: %1d IP: %s",
ci->client_index, ci->client_name,
ci->client_playas + (IsValidPlayer(ci->client_playas) ? 1 : 0),
GetPlayerIP(ci));
for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
if (ci->client_index != NETWORK_EMPTY_INDEX) {
IConsolePrintF(8, "Client #%1d name: '%s' company: %1d IP: %s",
ci->client_index, ci->client_name, ci->client_playas, GetPlayerIP(ci));
}
}
return true;
@@ -741,39 +705,30 @@ DEF_CONSOLE_CMD(ConNetworkConnect)
if (argc == 0) {
IConsoleHelp("Connect to a remote OTTD server and join the game. Usage: 'connect <ip>'");
IConsoleHelp("IP can contain port and player: 'IP[[#Player]:Port]', eg: 'server.ottd.org#2:443'");
IConsoleHelp("Player #255 is spectator all others are a certain company with Company 1 being #1");
IConsoleHelp("IP can contain port and player: 'IP#Player:Port', eg: 'server.ottd.org#2:443'");
return true;
}
if (argc < 2) return false;
if (_networking) NetworkDisconnect(); // we are in network-mode, first close it!
if (_networking) // We are in network-mode, first close it!
NetworkDisconnect();
ip = argv[1];
/* Default settings: default port and new company */
rport = NETWORK_DEFAULT_PORT;
_network_playas = PLAYER_NEW_COMPANY;
ParseConnectionString(&player, &port, ip);
IConsolePrintF(_icolour_def, "Connecting to %s...", ip);
if (player != NULL) {
_network_playas = atoi(player);
IConsolePrintF(_icolour_def, " player-no: %s", player);
}
if (port != NULL) {
rport = atoi(port);
IConsolePrintF(_icolour_def, " port: %s", port);
}
if (player != NULL) {
_network_playas = atoi(player);
IConsolePrintF(_icolour_def, " player-no: %d", _network_playas);
/* From a user pov 0 is a new player, internally it's different and all
* players are offset by one to ease up on users (eg players 1-8 not 0-7) */
if (_network_playas != PLAYER_SPECTATOR) {
_network_playas--;
if (!IsValidPlayer(_network_playas)) return false;
}
}
NetworkClientConnectGame(ip, rport);
return true;
@@ -887,44 +842,17 @@ DEF_CONSOLE_CMD(ConEchoC)
return true;
}
extern void SwitchMode(int new_mode);
DEF_CONSOLE_CMD(ConNewGame)
{
if (argc == 0) {
IConsoleHelp("Start a new game. Usage: 'newgame [seed]'");
IConsoleHelp("The server can force a new game using 'newgame'; any client joined will rejoin after the server is done generating the new game.");
IConsoleHelp("Start a new game. Usage: 'newgame'");
IConsoleHelp("The server can force a new game using 'newgame', any client using it will part and start a single-player game");
return true;
}
StartNewGameWithoutGUI((argc == 2) ? (uint)atoi(argv[1]) : GENERATE_NEW_SEED);
return true;
}
extern void SwitchMode(int new_mode);
DEF_CONSOLE_CMD(ConRestart)
{
if (argc == 0) {
IConsoleHelp("Restart game. Usage: 'restart'");
IConsoleHelp("Restarts a game. It tries to reproduce the exact same map as the game started with.");
return true;
}
/* Don't copy the _newgame pointers to the real pointers, so call SwitchMode directly */
_patches.map_x = MapLogX();
_patches.map_y = FindFirstBit(MapSizeY());
SwitchMode(SM_NEWGAME);
return true;
}
DEF_CONSOLE_CMD(ConGetSeed)
{
if (argc == 0) {
IConsoleHelp("Returns the seed used to create this game. Usage: 'getseed'");
IConsoleHelp("The seed can be used to reproduce the exact same map as the game started with.");
return true;
}
IConsolePrintF(_icolour_def, "Generation Seed: %u", _patches.generation_seed);
GenRandomNewGame(Random(), InteractiveRandom());
return true;
}
@@ -959,10 +887,10 @@ DEF_CONSOLE_CMD(ConScreenShot)
if (argc > 3) return false;
SetScreenshotType(SC_VIEWPORT);
_make_screenshot = 1;
if (argc > 1) {
if (strcmp(argv[1], "big") == 0 || (argc == 3 && strcmp(argv[2], "big") == 0))
SetScreenshotType(SC_WORLD);
_make_screenshot = 2;
if (strcmp(argv[1], "no_con") == 0 || (argc == 3 && strcmp(argv[2], "no_con") == 0))
IConsoleClose();
@@ -1039,9 +967,7 @@ DEF_CONSOLE_CMD(ConDebugLevel)
if (argc == 1) {
IConsolePrintF(_icolour_def, "Current debug-level: '%s'", GetDebugString());
} else {
SetDebugString(argv[1]);
}
} else SetDebugString(argv[1]);
return true;
}
@@ -1193,44 +1119,8 @@ DEF_CONSOLE_CMD(ConSay)
if (!_network_server) {
SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0 /* param does not matter */, argv[1]);
} else {
} else
NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, argv[1], NETWORK_SERVER_INDEX);
}
return true;
}
#ifdef ENABLE_NETWORK
#include "table/strings.h"
#endif /* ENABLE_NETWORK */
DEF_CONSOLE_CMD(ConPlayers)
{
Player *p;
if (argc == 0) {
IConsoleHelp("List the in-game details of all clients connected to the server. Usage 'players'");
return true;
}
NetworkPopulateCompanyInfo();
FOR_ALL_PLAYERS(p) {
char buffer[512];
const NetworkPlayerInfo *npi;
if (!p->is_active) continue;
npi = &_network_player_info[p->index];
GetString(buffer, STR_00D1_DARK_BLUE + _player_colors[p->index], lastof(buffer));
IConsolePrintF(8, "#:%d(%s) Company Name: '%s' Year Founded: %d Money: %d Loan: %d Value: %" OTTD_PRINTF64 "d (T:%d, R:%d, P:%d, S:%d) %sprotected",
p->index + 1, buffer, npi->company_name, p->inaugurated_year, p->player_money, p->current_loan, CalculateCompanyValue(p),
/* trains */ npi->num_vehicle[0],
/* lorry + bus */ npi->num_vehicle[1] + npi->num_vehicle[2],
/* planes */ npi->num_vehicle[3],
/* ships */ npi->num_vehicle[4],
/* protected */ npi->password[0] == '\0' ? "un" : "");
}
return true;
}
@@ -1251,10 +1141,9 @@ DEF_CONSOLE_CMD(ConSayPlayer)
}
if (!_network_server) {
SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, atoi(argv[1]), argv[2]);
} else {
NetworkServer_HandleChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
}
SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_PLAYER, DESTTYPE_PLAYER, atoi(argv[1]), argv[2]);
} else
NetworkServer_HandleChat(NETWORK_ACTION_CHAT_PLAYER, DESTTYPE_PLAYER, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
return true;
}
@@ -1271,16 +1160,15 @@ DEF_CONSOLE_CMD(ConSayClient)
if (!_network_server) {
SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2]);
} else {
} else
NetworkServer_HandleChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
}
return true;
}
DEF_CONSOLE_HOOK(ConHookServerPW)
{
if (strcmp(_network_server_password, "*") == 0) {
if (strncmp(_network_server_password, "*", NETWORK_PASSWORD_LENGTH) == 0) {
_network_server_password[0] = '\0';
_network_game_info.use_password = 0;
} else {
@@ -1293,7 +1181,7 @@ DEF_CONSOLE_HOOK(ConHookServerPW)
DEF_CONSOLE_HOOK(ConHookRconPW)
{
if (strcmp(_network_rcon_password, "*") == 0)
if (strncmp(_network_rcon_password, "*", NETWORK_PASSWORD_LENGTH) == 0)
_network_rcon_password[0] = '\0';
ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_game_info.rcon_password));
@@ -1305,19 +1193,20 @@ DEF_CONSOLE_HOOK(ConHookRconPW)
bool NetworkChangeCompanyPassword(byte argc, char *argv[])
{
if (argc == 0) {
if (!IsValidPlayer(_local_player)) return true; // dedicated server
if (_local_player >= MAX_PLAYERS) return true; // dedicated server
IConsolePrintF(_icolour_warn, "Current value for 'company_pw': %s", _network_player_info[_local_player].password);
return true;
}
if (!IsValidPlayer(_local_player)) {
if (_local_player >= MAX_PLAYERS) {
IConsoleError("You have to own a company to make use of this command.");
return false;
}
if (argc != 1) return false;
if (strcmp(argv[0], "*") == 0) argv[0][0] = '\0';
if (strncmp(argv[0], "*", sizeof(_network_player_info[_local_player].password)) == 0)
argv[0][0] = '\0';
ttd_strlcpy(_network_player_info[_local_player].password, argv[0], sizeof(_network_player_info[_local_player].password));
@@ -1379,7 +1268,6 @@ DEF_CONSOLE_CMD(ConProcServerIP)
IConsolePrintF(_icolour_warn, "'server_ip' changed to: %s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
return true;
}
#endif /* ENABLE_NETWORK */
DEF_CONSOLE_CMD(ConPatch)
{
@@ -1393,31 +1281,12 @@ DEF_CONSOLE_CMD(ConPatch)
if (argc == 2) {
IConsoleGetPatchSetting(argv[1]);
} else {
uint32 val;
if (GetArgumentInteger(&val, argv[2])) {
if (!IConsoleSetPatchSetting(argv[1], val)) {
IConsoleError("This command/variable is only available to a network server.");
}
}
}
} else
IConsoleSetPatchSetting(argv[1], argv[2]);
return true;
}
DEF_CONSOLE_CMD(ConListPatches)
{
if (argc == 0) {
IConsoleHelp("List patch options. Usage: 'list_patches'");
return true;
}
if (argc != 1) return false;
IConsoleListPatches();
return true;
}
#endif /* ENABLE_NETWORK */
DEF_CONSOLE_CMD(ConListDumpVariables)
{
@@ -1482,8 +1351,6 @@ void IConsoleStdLibRegister(void)
IConsoleCmdRegister("list_vars", ConListVariables);
IConsoleCmdRegister("list_aliases", ConListAliases);
IConsoleCmdRegister("newgame", ConNewGame);
IConsoleCmdRegister("restart", ConRestart);
IConsoleCmdRegister("getseed", ConGetSeed);
IConsoleCmdRegister("quit", ConExit);
IConsoleCmdRegister("resetengines", ConResetEngines);
IConsoleCmdRegister("return", ConReturn);
@@ -1498,8 +1365,6 @@ void IConsoleStdLibRegister(void)
IConsoleCmdRegister("cd", ConChangeDirectory);
IConsoleCmdRegister("pwd", ConPrintWorkingDirectory);
IConsoleCmdRegister("clear", ConClearBuffer);
IConsoleCmdRegister("patch", ConPatch);
IConsoleCmdRegister("list_patches", ConListPatches);
IConsoleAliasRegister("dir", "ls");
IConsoleAliasRegister("del", "rm %+");
@@ -1518,8 +1383,6 @@ void IConsoleStdLibRegister(void)
/*** Networking commands ***/
IConsoleCmdRegister("say", ConSay);
IConsoleCmdHookAdd("say", ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
IConsoleCmdRegister("players", ConPlayers);
IConsoleCmdHookAdd("players", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
IConsoleCmdRegister("say_player", ConSayPlayer);
IConsoleCmdHookAdd("say_player", ICONSOLE_HOOK_ACCESS, ConHookNeedNetwork);
IConsoleCmdRegister("say_client", ConSayClient);
@@ -1555,6 +1418,9 @@ void IConsoleStdLibRegister(void)
IConsoleCmdRegister("unpause", ConUnPauseGame);
IConsoleCmdHookAdd("unpause", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
IConsoleCmdRegister("patch", ConPatch);
IConsoleCmdHookAdd("patch", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
/*** Networking variables ***/
IConsoleVarRegister("net_frame_freq", &_network_frame_freq, ICONSOLE_VAR_BYTE, "The amount of frames before a command will be (visibly) executed. Default value: 1");
IConsoleVarHookAdd("net_frame_freq", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
@@ -1612,12 +1478,8 @@ void IConsoleStdLibRegister(void)
IConsoleVarHookAdd("autoclean_protected", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
IConsoleVarRegister("autoclean_unprotected", &_network_autoclean_unprotected, ICONSOLE_VAR_BYTE, "Automatically shut down inactive companies after the given amount of months");
IConsoleVarHookAdd("autoclean_unprotected", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
IConsoleVarRegister("restart_game_year", &_network_restart_game_year, ICONSOLE_VAR_UINT16, "Auto-restart the server when Jan 1st of the set year is reached. Use '0' to disable this");
IConsoleVarHookAdd("restart_game_year", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
IConsoleVarRegister("min_players", &_network_min_players, ICONSOLE_VAR_BYTE, "Automatically pause the game when the number of active players passes below the given amount");
IConsoleVarHookAdd("min_players", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
IConsoleVarHookAdd("min_players", ICONSOLE_HOOK_POST_ACTION, ConHookCheckMinPlayers);
IConsoleVarRegister("restart_game_date", &_network_restart_game_date, ICONSOLE_VAR_UINT16, "Auto-restart the server when Jan 1st of the set year is reached. Use '0' to disable this");
IConsoleVarHookAdd("restart_game_date", ICONSOLE_HOOK_ACCESS, ConHookServerOnly);
#endif /* ENABLE_NETWORK */

View File

@@ -6,182 +6,95 @@
#include "news.h"
#include "variables.h"
#include "table/strings.h"
#include "date.h"
// exchange rate prefix symbol_pos
// | separator | postfix |
// | | Euro year | | | name
// | | | | | | |
static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = {
{ 1, ',', CF_NOEURO, "£", "", 0, STR_CURR_GBP }, // british pounds
{ 2, ',', CF_NOEURO, "$", "", 0, STR_CURR_USD }, // us dollars
{ 2, ',', CF_ISEURO, "", "", 0, STR_CURR_EUR }, // Euro
{ 220, ',', CF_NOEURO, "¥", "", 0, STR_CURR_YEN }, // yen
{ 20, ',', 2002, "", " S.", 1, STR_CURR_ATS }, // austrian schilling
{ 59, ',', 2002, "BEF ", "", 0, STR_CURR_BEF }, // belgian franc
{ 2, ',', CF_NOEURO, "CHF ", "", 0, STR_CURR_CHF }, // swiss franc
{ 41, ',', CF_NOEURO, "", " Kč", 1, STR_CURR_CZK }, // czech koruna
{ 3, '.', 2002, "DM ", "", 0, STR_CURR_DEM }, // deutsche mark
{ 11, '.', CF_NOEURO, "", " kr", 1, STR_CURR_DKK }, // danish krone
{ 245, '.', 2002, "Pts ", "", 0, STR_CURR_ESP }, // spanish pesetas
{ 9, ',', 2002, "", " mk", 1, STR_CURR_FIM }, // finnish markka
{ 10, '.', 2002, "FF ", "", 0, STR_CURR_FRF }, // french francs
{ 500, ',', 2002, "", "Dr.", 1, STR_CURR_GRD }, // greek drachma
{ 378, ',', 2010, "", " Ft", 1, STR_CURR_HUF }, // hungarian forint
{ 130, '.', CF_NOEURO, "", " Kr", 1, STR_CURR_ISK }, // icelandic krona
{ 2850, ',', 2002, "", " L.", 1, STR_CURR_ITL }, // italian lira
{ 3, ',', 2002, "NLG ", "", 0, STR_CURR_NLG }, // dutch gulden
{ 12, '.', CF_NOEURO, "", " Kr", 1, STR_CURR_NOK }, // norwegian krone
{ 6, ' ', CF_NOEURO, "", " zl", 1, STR_CURR_PLN }, // polish zloty
{ 5, '.', CF_NOEURO, "", " Lei", 1, STR_CURR_ROL }, // romanian Lei
{ 50, ' ', CF_NOEURO, "", " p", 1, STR_CURR_RUR }, // russian rouble
{ 352, '.', CF_NOEURO, "", " SIT", 1, STR_CURR_SIT }, // slovenian tolar
{ 13, '.', CF_NOEURO, "", " Kr", 1, STR_CURR_SEK }, // swedish krona
{ 3, '.', CF_NOEURO, "", " YTL", 1, STR_CURR_YTL }, // turkish lira
{ 52, ',', CF_NOEURO, "", " Sk", 1, STR_CURR_SKK }, // slovak koruna
{ 4, ',', CF_NOEURO, "R$ ", "", 0, STR_CURR_BRR }, // brazil real
{ 1, ' ', CF_NOEURO, "", "", 2, STR_CURR_CUSTOM }, // custom currency
// exchange rate prefix
// | separator | postfix
// | | Euro year | |
// | | | | |
CurrencySpec _currency_specs[] = {
{ 1, ',', CF_NOEURO, "\xA3", "" }, // british pounds
{ 2, ',', CF_NOEURO, "$", "" }, // us dollars
{ 2, ',', CF_ISEURO, "<EFBFBD>", "" }, // Euro
{ 200, ',', CF_NOEURO, "\xA5", "" }, // yen
{ 19, ',', 2002, "", " S." }, // austrian schilling
{ 57, ',', 2002, "BEF ", "" }, // belgian franc
{ 2, ',', CF_NOEURO, "CHF ", "" }, // swiss franc
{ 50, ',', CF_NOEURO, "", " Kc" }, // czech koruna // TODO: Should use the "c" with an upside down "^"
{ 4, '.', 2002, "DM ", "" }, // deutsche mark
{ 10, '.', CF_NOEURO, "", " kr" }, // danish krone
{ 200, '.', 2002, "Pts ", "" }, // spanish pesetas
{ 8, ',', 2002, "", " mk" }, // finnish markka
{ 10, '.', 2002, "FF ", "" }, // french francs
{ 480, ',', 2002, "", "Dr." }, // greek drachma
{ 376, ',', 2002, "", " Ft" }, // hungarian forint
{ 130, '.', CF_NOEURO, "", " Kr" }, // icelandic krona
{ 2730, ',', 2002, "", " L." }, // italian lira
{ 3, ',', 2002, "NLG ", "" }, // dutch gulden
{ 11, '.', CF_NOEURO, "", " Kr" }, // norwegian krone
{ 6, ' ', CF_NOEURO, "", " zl" }, // polish zloty
{ 6, '.', CF_NOEURO, "", " Lei" }, // romanian Lei
{ 5, ' ', CF_NOEURO, "", " p" }, // russian rouble
{ 13, '.', CF_NOEURO, "", " Kr" }, // swedish krona
{ 1, ' ', CF_NOEURO, "", "" }, // custom currency
};
/* Array of currencies used by the system */
CurrencySpec _currency_specs[NUM_CURRENCY];
/**
* These enums are only declared in order to make sens
* out of the TTDPatch_To_OTTDIndex array that will follow
* Every currency used by Ottd is there, just in case TTDPatch will
* add those missing in its code
**/
enum {
CURR_GBP,
CURR_USD,
CURR_EUR,
CURR_YEN,
CURR_ATS,
CURR_BEF,
CURR_CHF,
CURR_CZK,
CURR_DEM,
CURR_DKK,
CURR_ESP,
CURR_FIM,
CURR_FRF,
CURR_GRD,
CURR_HUF,
CURR_ISK,
CURR_ITL,
CURR_NLG,
CURR_NOK,
CURR_PLN,
CURR_ROL,
CURR_RUR,
CURR_SIT,
CURR_SEK,
CURR_YTL,
const StringID _currency_string_list[] = {
STR_CURR_GBP,
STR_CURR_USD,
STR_CURR_EUR,
STR_CURR_YEN,
STR_CURR_ATS,
STR_CURR_BEF,
STR_CURR_CHF,
STR_CURR_CZK,
STR_CURR_DEM,
STR_CURR_DKK,
STR_CURR_ESP,
STR_CURR_FIM,
STR_CURR_FRF,
STR_CURR_GRD,
STR_CURR_HUF,
STR_CURR_ISK,
STR_CURR_ITL,
STR_CURR_NLG,
STR_CURR_NOK,
STR_CURR_PLN,
STR_CURR_ROL,
STR_CURR_RUR,
STR_CURR_SEK,
STR_CURR_CUSTOM,
INVALID_STRING_ID
};
/**
* This array represent the position of OpenTTD's currencies,
* compared to TTDPatch's ones.
* When a grf sends currencies, they are based on the order defined by TTDPatch.
* So, we must reindex them to our own order.
**/
const byte TTDPatch_To_OTTDIndex[] =
{
CURR_GBP,
CURR_USD,
CURR_FRF,
CURR_DEM,
CURR_YEN,
CURR_ESP,
CURR_HUF,
CURR_PLN,
CURR_ATS,
CURR_BEF,
CURR_DKK,
CURR_FIM,
CURR_GRD,
CURR_CHF,
CURR_NLG,
CURR_ITL,
CURR_SEK,
CURR_RUR,
CURR_EUR,
};
// NOTE: Make sure both lists are in the same order
// + 1 string list terminator
assert_compile(lengthof(_currency_specs) + 1 == lengthof(_currency_string_list));
/**
* Will return the ottd's index correspondance to
* the ttdpatch's id. If the id is bigger then the array,
* it is a grf written for ottd, thus returning the same id.
* Only called from newgrf.c
* @param grfcurr_id currency id coming from newgrf
* @return the corrected index
**/
byte GetNewgrfCurrencyIdConverted(byte grfcurr_id)
{
return (grfcurr_id >= lengthof(TTDPatch_To_OTTDIndex)) ? grfcurr_id : TTDPatch_To_OTTDIndex[grfcurr_id];
}
/* get a mask of the allowed currencies depending on the year */
// get a mask of the allowed currencies depending on the year
uint GetMaskOfAllowedCurrencies(void)
{
uint mask = 0;
uint i;
for (i = 0; i < NUM_CURRENCY; i++) {
Year to_euro = _currency_specs[i].to_euro;
for (i = 0; i != lengthof(_currency_specs); i++) {
uint16 to_euro = _currency_specs[i].to_euro;
if (to_euro != CF_NOEURO && to_euro != CF_ISEURO && _cur_year >= to_euro) continue;
if (to_euro == CF_ISEURO && _cur_year < 2000) continue;
if (to_euro != CF_NOEURO && to_euro != CF_ISEURO && _cur_year >= to_euro - MAX_YEAR_BEGIN_REAL) continue;
if (to_euro == CF_ISEURO && _cur_year < 2000 - MAX_YEAR_BEGIN_REAL) continue;
mask |= (1 << i);
}
mask |= (1 << CUSTOM_CURRENCY_ID); // always allow custom currency
mask |= (1 << 23); // always allow custom currency
return mask;
}
/**
* Verify if the currency chosen by the user is about to be converted to Euro
**/
void CheckSwitchToEuro(void)
{
if (_currency_specs[_opt.currency].to_euro != CF_NOEURO &&
_currency_specs[_opt.currency].to_euro != CF_ISEURO &&
_cur_year >= _currency_specs[_opt.currency].to_euro) {
MAX_YEAR_BEGIN_REAL + _cur_year >= _currency_specs[_opt.currency].to_euro) {
_opt.currency = 2; // this is the index of euro above.
AddNewsItem(STR_EURO_INTRODUCE, NEWS_FLAGS(NM_NORMAL, 0, NT_ECONOMY, 0), 0, 0);
}
}
/**
* Will fill _currency_specs array with
* default values from origin_currency_specs
* Called only from newgrf.c and settings.c.
* @param preserve_custom will not reset custom currency (the latest one on the list)
* if ever it is flagged to true. In which case, the total size of the memory to move
* will be one currency spec less, thus preserving the custom curreny from been
* overwritten.void ResetCurrencies(bool preserve_custom)
*/
void ResetCurrencies(bool preserve_custom)
{
memcpy(&_currency_specs, &origin_currency_specs, sizeof(origin_currency_specs) - (preserve_custom ? sizeof(_custom_currency) : 0));
}
/**
* Build a list of currency names StringIDs to use in a dropdown list
* @return Pointer to a (static) array of StringIDs
*/
StringID* BuildCurrencyDropdown(void)
{
/* Allow room for all currencies, plus a terminator entry */
static StringID names[NUM_CURRENCY + 1];
uint i;
/* Add each name */
for (i = 0; i < NUM_CURRENCY; i++) {
names[i] = _currency_specs[i].name;
}
/* Terminate the list */
names[i] = INVALID_STRING_ID;
return names;
}

View File

@@ -6,40 +6,24 @@
enum {
CF_NOEURO = 0,
CF_ISEURO = 1,
NUM_CURRENCY = 28,
CUSTOM_CURRENCY_ID = NUM_CURRENCY - 1
};
typedef struct {
uint16 rate;
char separator;
Year to_euro;
uint16 to_euro;
char prefix[16];
char suffix[16];
/**
* The currency symbol is represented by two possible values, prefix and suffix
* Usage of one or the other is determined by symbol_pos.
* 0 = prefix
* 1 = suffix
* 2 = both : Special case only for custom currency.
* It is not a spec from Newgrf,
* rather a way to let users do what they want with custom curency
*/
byte symbol_pos;
StringID name;
} CurrencySpec;
extern CurrencySpec _currency_specs[NUM_CURRENCY];
extern CurrencySpec _currency_specs[];
extern const StringID _currency_string_list[];
// XXX small hack, but makes the rest of the code a bit nicer to read
#define _custom_currency (_currency_specs[CUSTOM_CURRENCY_ID])
#define _custom_currency (_currency_specs[23])
#define _currency ((const CurrencySpec*)&_currency_specs[_opt_ptr->currency])
uint GetMaskOfAllowedCurrencies(void);
void CheckSwitchToEuro(void);
void ResetCurrencies(bool preserve_custom);
StringID* BuildCurrencyDropdown(void);
byte GetNewgrfCurrencyIdConverted(byte grfcurr_id);
#endif /* CURRENCY_H */

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

304
date.c
View File

@@ -1,304 +0,0 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "date.h"
#include "variables.h"
#include "macros.h"
#include "vehicle.h"
#include "network.h"
#include "network_data.h"
#include "network_server.h"
#include "functions.h"
#include "currency.h"
Year _cur_year;
Month _cur_month;
Date _date;
DateFract _date_fract;
void SetDate(Date date)
{
YearMonthDay ymd;
_date = date;
ConvertDateToYMD(date, &ymd);
_cur_year = ymd.year;
_cur_month = ymd.month;
#ifdef ENABLE_NETWORK
_network_last_advertise_frame = 0;
_network_need_advertise = true;
#endif /* ENABLE_NETWORK */
}
#define M(a, b) ((a << 5) | b)
static const uint16 _month_date_from_year_day[] = {
M( 0, 1), M( 0, 2), M( 0, 3), M( 0, 4), M( 0, 5), M( 0, 6), M( 0, 7), M( 0, 8), M( 0, 9), M( 0, 10), M( 0, 11), M( 0, 12), M( 0, 13), M( 0, 14), M( 0, 15), M( 0, 16), M( 0, 17), M( 0, 18), M( 0, 19), M( 0, 20), M( 0, 21), M( 0, 22), M( 0, 23), M( 0, 24), M( 0, 25), M( 0, 26), M( 0, 27), M( 0, 28), M( 0, 29), M( 0, 30), M( 0, 31),
M( 1, 1), M( 1, 2), M( 1, 3), M( 1, 4), M( 1, 5), M( 1, 6), M( 1, 7), M( 1, 8), M( 1, 9), M( 1, 10), M( 1, 11), M( 1, 12), M( 1, 13), M( 1, 14), M( 1, 15), M( 1, 16), M( 1, 17), M( 1, 18), M( 1, 19), M( 1, 20), M( 1, 21), M( 1, 22), M( 1, 23), M( 1, 24), M( 1, 25), M( 1, 26), M( 1, 27), M( 1, 28), M( 1, 29),
M( 2, 1), M( 2, 2), M( 2, 3), M( 2, 4), M( 2, 5), M( 2, 6), M( 2, 7), M( 2, 8), M( 2, 9), M( 2, 10), M( 2, 11), M( 2, 12), M( 2, 13), M( 2, 14), M( 2, 15), M( 2, 16), M( 2, 17), M( 2, 18), M( 2, 19), M( 2, 20), M( 2, 21), M( 2, 22), M( 2, 23), M( 2, 24), M( 2, 25), M( 2, 26), M( 2, 27), M( 2, 28), M( 2, 29), M( 2, 30), M( 2, 31),
M( 3, 1), M( 3, 2), M( 3, 3), M( 3, 4), M( 3, 5), M( 3, 6), M( 3, 7), M( 3, 8), M( 3, 9), M( 3, 10), M( 3, 11), M( 3, 12), M( 3, 13), M( 3, 14), M( 3, 15), M( 3, 16), M( 3, 17), M( 3, 18), M( 3, 19), M( 3, 20), M( 3, 21), M( 3, 22), M( 3, 23), M( 3, 24), M( 3, 25), M( 3, 26), M( 3, 27), M( 3, 28), M( 3, 29), M( 3, 30),
M( 4, 1), M( 4, 2), M( 4, 3), M( 4, 4), M( 4, 5), M( 4, 6), M( 4, 7), M( 4, 8), M( 4, 9), M( 4, 10), M( 4, 11), M( 4, 12), M( 4, 13), M( 4, 14), M( 4, 15), M( 4, 16), M( 4, 17), M( 4, 18), M( 4, 19), M( 4, 20), M( 4, 21), M( 4, 22), M( 4, 23), M( 4, 24), M( 4, 25), M( 4, 26), M( 4, 27), M( 4, 28), M( 4, 29), M( 4, 30), M( 4, 31),
M( 5, 1), M( 5, 2), M( 5, 3), M( 5, 4), M( 5, 5), M( 5, 6), M( 5, 7), M( 5, 8), M( 5, 9), M( 5, 10), M( 5, 11), M( 5, 12), M( 5, 13), M( 5, 14), M( 5, 15), M( 5, 16), M( 5, 17), M( 5, 18), M( 5, 19), M( 5, 20), M( 5, 21), M( 5, 22), M( 5, 23), M( 5, 24), M( 5, 25), M( 5, 26), M( 5, 27), M( 5, 28), M( 5, 29), M( 5, 30),
M( 6, 1), M( 6, 2), M( 6, 3), M( 6, 4), M( 6, 5), M( 6, 6), M( 6, 7), M( 6, 8), M( 6, 9), M( 6, 10), M( 6, 11), M( 6, 12), M( 6, 13), M( 6, 14), M( 6, 15), M( 6, 16), M( 6, 17), M( 6, 18), M( 6, 19), M( 6, 20), M( 6, 21), M( 6, 22), M( 6, 23), M( 6, 24), M( 6, 25), M( 6, 26), M( 6, 27), M( 6, 28), M( 6, 29), M( 6, 30), M( 6, 31),
M( 7, 1), M( 7, 2), M( 7, 3), M( 7, 4), M( 7, 5), M( 7, 6), M( 7, 7), M( 7, 8), M( 7, 9), M( 7, 10), M( 7, 11), M( 7, 12), M( 7, 13), M( 7, 14), M( 7, 15), M( 7, 16), M( 7, 17), M( 7, 18), M( 7, 19), M( 7, 20), M( 7, 21), M( 7, 22), M( 7, 23), M( 7, 24), M( 7, 25), M( 7, 26), M( 7, 27), M( 7, 28), M( 7, 29), M( 7, 30), M( 7, 31),
M( 8, 1), M( 8, 2), M( 8, 3), M( 8, 4), M( 8, 5), M( 8, 6), M( 8, 7), M( 8, 8), M( 8, 9), M( 8, 10), M( 8, 11), M( 8, 12), M( 8, 13), M( 8, 14), M( 8, 15), M( 8, 16), M( 8, 17), M( 8, 18), M( 8, 19), M( 8, 20), M( 8, 21), M( 8, 22), M( 8, 23), M( 8, 24), M( 8, 25), M( 8, 26), M( 8, 27), M( 8, 28), M( 8, 29), M( 8, 30),
M( 9, 1), M( 9, 2), M( 9, 3), M( 9, 4), M( 9, 5), M( 9, 6), M( 9, 7), M( 9, 8), M( 9, 9), M( 9, 10), M( 9, 11), M( 9, 12), M( 9, 13), M( 9, 14), M( 9, 15), M( 9, 16), M( 9, 17), M( 9, 18), M( 9, 19), M( 9, 20), M( 9, 21), M( 9, 22), M( 9, 23), M( 9, 24), M( 9, 25), M( 9, 26), M( 9, 27), M( 9, 28), M( 9, 29), M( 9, 30), M( 9, 31),
M(10, 1), M(10, 2), M(10, 3), M(10, 4), M(10, 5), M(10, 6), M(10, 7), M(10, 8), M(10, 9), M(10, 10), M(10, 11), M(10, 12), M(10, 13), M(10, 14), M(10, 15), M(10, 16), M(10, 17), M(10, 18), M(10, 19), M(10, 20), M(10, 21), M(10, 22), M(10, 23), M(10, 24), M(10, 25), M(10, 26), M(10, 27), M(10, 28), M(10, 29), M(10, 30),
M(11, 1), M(11, 2), M(11, 3), M(11, 4), M(11, 5), M(11, 6), M(11, 7), M(11, 8), M(11, 9), M(11, 10), M(11, 11), M(11, 12), M(11, 13), M(11, 14), M(11, 15), M(11, 16), M(11, 17), M(11, 18), M(11, 19), M(11, 20), M(11, 21), M(11, 22), M(11, 23), M(11, 24), M(11, 25), M(11, 26), M(11, 27), M(11, 28), M(11, 29), M(11, 30), M(11, 31),
};
#undef M
enum {
ACCUM_JAN = 0,
ACCUM_FEB = ACCUM_JAN + 31,
ACCUM_MAR = ACCUM_FEB + 29,
ACCUM_APR = ACCUM_MAR + 31,
ACCUM_MAY = ACCUM_APR + 30,
ACCUM_JUN = ACCUM_MAY + 31,
ACCUM_JUL = ACCUM_JUN + 30,
ACCUM_AUG = ACCUM_JUL + 31,
ACCUM_SEP = ACCUM_AUG + 31,
ACCUM_OCT = ACCUM_SEP + 30,
ACCUM_NOV = ACCUM_OCT + 31,
ACCUM_DEC = ACCUM_NOV + 30,
};
static const uint16 _accum_days_for_month[] = {
ACCUM_JAN, ACCUM_FEB, ACCUM_MAR, ACCUM_APR,
ACCUM_MAY, ACCUM_JUN, ACCUM_JUL, ACCUM_AUG,
ACCUM_SEP, ACCUM_OCT, ACCUM_NOV, ACCUM_DEC,
};
static inline bool IsLeapYear(Year yr)
{
return yr % 4 == 0 && (yr % 100 != 0 || yr % 400 == 0);
}
/**
* Converts a Date to a Year, Month & Day.
* @param date the date to convert from
* @param ymd the year, month and day to write to
*/
void ConvertDateToYMD(Date date, YearMonthDay *ymd)
{
/*
* Year determination in multiple steps to account for leap
* years. First do the large steps, then the smaller ones.
*/
/* There are 97 leap years in 400 years */
Year yr = 400 * (date / (365 * 400 + 97));
int rem = date % (365 * 400 + 97);
uint16 x;
if (rem >= 365 * 100 + 25) {
/* There are 25 leap years in the first 100 years after
* every 400th year, as every 400th year is a leap year */
yr += 100;
rem -= 365 * 100 + 25;
/* There are 24 leap years in the next couple of 100 years */
yr += 100 * (rem / (365 * 100 + 24));
rem = (rem % (365 * 100 + 24));
}
if (!IsLeapYear(yr) && rem >= 365 * 4) {
/* The first 4 year of the century are not always a leap year */
yr += 4;
rem -= 365 * 4;
}
/* There is 1 leap year every 4 years */
yr += 4 * (rem / (365 * 4 + 1));
rem = rem % (365 * 4 + 1);
/* The last (max 3) years to account for; the first one
* can be, but is not necessarily a leap year */
while (rem >= (IsLeapYear(yr) ? 366 : 365)) {
rem -= IsLeapYear(yr) ? 366 : 365;
yr++;
}
/* Skip the 29th of February in non-leap years */
if (!IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++;
ymd->year = yr;
x = _month_date_from_year_day[rem];
ymd->month = x >> 5;
ymd->day = x & 0x1F;
}
/**
* Converts a tupe of Year, Month and Day to a Date.
* @param year is a number between 0..MAX_YEAR
* @param month is a number between 0..11
* @param day is a number between 1..31
*/
Date ConvertYMDToDate(Year year, Month month, Day day)
{
/*
* Each passed leap year adds one day to the 'day count'.
*
* A special case for the year 0 as no year has been passed,
* but '(year - 1) / 4' does not yield '-1' to counteract the
* '+1' at the end of the formula as divisions round to zero.
*/
int nr_of_leap_years = (year == 0) ? 0 : ((year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + 1);
/* Day-offset in a leap year */
int days = _accum_days_for_month[month] + day - 1;
/* Account for the missing of the 29th of February in non-leap years */
if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
return year * 365 + nr_of_leap_years + days;
}
/** Functions used by the IncreaseDate function */
extern void OnNewDay_Train(Vehicle *v);
extern void OnNewDay_RoadVeh(Vehicle *v);
extern void OnNewDay_Aircraft(Vehicle *v);
extern void OnNewDay_Ship(Vehicle *v);
static void OnNewDay_EffectVehicle(Vehicle *v) { /* empty */ }
extern void OnNewDay_DisasterVehicle(Vehicle *v);
typedef void OnNewVehicleDayProc(Vehicle *v);
static OnNewVehicleDayProc * _on_new_vehicle_day_proc[] = {
OnNewDay_Train,
OnNewDay_RoadVeh,
OnNewDay_Ship,
OnNewDay_Aircraft,
OnNewDay_EffectVehicle,
OnNewDay_DisasterVehicle,
};
extern void WaypointsDailyLoop(void);
extern void TextMessageDailyLoop(void);
extern void EnginesDailyLoop(void);
extern void DisasterDailyLoop(void);
extern void PlayersMonthlyLoop(void);
extern void EnginesMonthlyLoop(void);
extern void TownsMonthlyLoop(void);
extern void IndustryMonthlyLoop(void);
extern void StationMonthlyLoop(void);
extern void PlayersYearlyLoop(void);
extern void TrainsYearlyLoop(void);
extern void RoadVehiclesYearlyLoop(void);
extern void AircraftYearlyLoop(void);
extern void ShipsYearlyLoop(void);
extern void ShowEndGameChart(void);
static const Month _autosave_months[] = {
0, // never
1, // every month
3, // every 3 months
6, // every 6 months
12, // every 12 months
};
/**
* Runs the day_proc for every DAY_TICKS vehicle starting at daytick.
*/
static void RunVehicleDayProc(uint daytick)
{
uint total = GetMaxVehicleIndex() + 1;
uint i;
for (i = daytick; i < total; i += DAY_TICKS) {
Vehicle *v = GetVehicle(i);
if (IsValidVehicle(v)) _on_new_vehicle_day_proc[v->type - 0x10](v);
}
}
void IncreaseDate(void)
{
YearMonthDay ymd;
if (_game_mode == GM_MENU) {
_tick_counter++;
return;
}
RunVehicleDayProc(_date_fract);
/* increase day, and check if a new day is there? */
_tick_counter++;
_date_fract++;
if (_date_fract < DAY_TICKS) return;
_date_fract = 0;
/* yeah, increase day counter and call various daily loops */
_date++;
TextMessageDailyLoop();
DisasterDailyLoop();
WaypointsDailyLoop();
if (_game_mode != GM_MENU) {
InvalidateWindowWidget(WC_STATUS_BAR, 0, 0);
EnginesDailyLoop();
}
/* check if we entered a new month? */
ConvertDateToYMD(_date, &ymd);
if (ymd.month == _cur_month) return;
_cur_month = ymd.month;
/* yes, call various monthly loops */
if (_game_mode != GM_MENU) {
if (_opt.autosave != 0 && (_cur_month % _autosave_months[_opt.autosave]) == 0) {
_do_autosave = true;
RedrawAutosave();
}
PlayersMonthlyLoop();
EnginesMonthlyLoop();
TownsMonthlyLoop();
IndustryMonthlyLoop();
StationMonthlyLoop();
if (_network_server) NetworkServerMonthlyLoop();
}
/* check if we entered a new year? */
if (ymd.year == _cur_year) return;
_cur_year = ymd.year;
/* yes, call various yearly loops */
PlayersYearlyLoop();
TrainsYearlyLoop();
RoadVehiclesYearlyLoop();
AircraftYearlyLoop();
ShipsYearlyLoop();
if (_network_server) NetworkServerYearlyLoop();
/* check if we reached end of the game */
if (_cur_year == _patches.ending_year) {
ShowEndGameChart();
/* check if we reached the maximum year, decrement dates by a year */
} else if (_cur_year == MAX_YEAR + 1) {
Vehicle *v;
uint days_this_year;
_cur_year--;
days_this_year = IsLeapYear(_cur_year) ? 366 : 365;
_date -= days_this_year;
FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year;
/* Because the _date wraps here, and text-messages expire by game-days, we have to clean out
* all of them if the date is set back, else those messages will hang for ever */
InitTextMessage();
}
if (_patches.auto_euro) CheckSwitchToEuro();
}

59
date.h
View File

@@ -1,59 +0,0 @@
/* $Id$ */
#ifndef DATE_H
#define DATE_H
/**
* 1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885. On
* an overflow the new day begun and 65535 / 885 = 74.
* 1 tick is approximately 30 ms.
* 1 day is thus about 2 seconds (74 * 30 = 2220) on a machine that can run OpenTTD normally
*/
#define DAY_TICKS 74
/*
* ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
* primarily used for loading newgrf and savegame data and returning some
* newgrf (callback) functions that were in the original (TTD) inherited
* format, where '_date == 0' meant that it was 1920-01-01.
*/
/** The minimum starting year/base year of the original TTD */
#define ORIGINAL_BASE_YEAR 1920
/** The maximum year of the original TTD */
#define ORIGINAL_MAX_YEAR 2090
/**
* The offset in days from the '_date == 0' till
* 'ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)'
*/
#define DAYS_TILL_ORIGINAL_BASE_YEAR (365 * ORIGINAL_BASE_YEAR + ORIGINAL_BASE_YEAR / 4 - ORIGINAL_BASE_YEAR / 100 + ORIGINAL_BASE_YEAR / 400)
/* The absolute minimum & maximum years in OTTD */
#define MIN_YEAR 0
/* MAX_YEAR, nicely rounded value of the number of years that can
* be encoded in a single 32 bits date, about 2^31 / 366 years. */
#define MAX_YEAR 5000000
/* Year and Date are defined elsewhere */
typedef uint8 Month;
typedef uint8 Day;
typedef uint16 DateFract;
typedef struct YearMonthDay {
Year year;
Month month;
Day day;
} YearMonthDay;
extern Year _cur_year;
extern Month _cur_month;
extern Date _date;
extern DateFract _date_fract;
void SetDate(Date date);
void ConvertDateToYMD(Date date, YearMonthDay *ymd);
Date ConvertYMDToDate(Year year, Month month, Day day);
#endif /* DATE_H */

View File

@@ -20,8 +20,6 @@ int _debug_spritecache_level;
int _debug_oldloader_level;
int _debug_ntp_level;
int _debug_npf_level;
int _debug_yapf_level;
int _debug_freetype_level;
void CDECL debug(const char *s, ...)
@@ -53,9 +51,7 @@ typedef struct DebugLevel {
DEBUG_LEVEL(spritecache),
DEBUG_LEVEL(oldloader),
DEBUG_LEVEL(ntp),
DEBUG_LEVEL(npf),
DEBUG_LEVEL(yapf),
DEBUG_LEVEL(freetype)
DEBUG_LEVEL(npf)
};
#undef DEBUG_LEVEL

25
debug.h
View File

@@ -19,8 +19,6 @@
extern int _debug_oldloader_level;
extern int _debug_ntp_level;
extern int _debug_npf_level;
extern int _debug_yapf_level;
extern int _debug_freetype_level;
#endif
void CDECL debug(const char *s, ...);
@@ -28,27 +26,4 @@ void CDECL debug(const char *s, ...);
void SetDebugString(const char *s);
const char *GetDebugString(void);
/* MSVCRT of course has to have a different syntax for long long *sigh* */
#if defined(_MSC_VER) || defined(__MINGW32__)
# define OTTD_PRINTF64 "I64"
#else
# define OTTD_PRINTF64 "ll"
#endif
// Used for profiling
#define TIC() {\
extern uint64 _rdtsc(void);\
uint64 _xxx_ = _rdtsc();\
static uint64 __sum__ = 0;\
static uint32 __i__ = 0;
#define TOC(str, count)\
__sum__ += _rdtsc() - _xxx_;\
if (++__i__ == count) {\
printf("[%s]: %" OTTD_PRINTF64 "u [avg: %.1f]\n", str, __sum__, __sum__/(double)__i__);\
__i__ = 0;\
__sum__ = 0;\
}\
}
#endif /* DEBUG_H */

78
depot.c
View File

@@ -10,20 +10,25 @@
#include "saveload.h"
#include "order.h"
enum {
/* Max depots: 64000 (8 * 8000) */
DEPOT_POOL_BLOCK_SIZE_BITS = 3, /* In bits, so (1 << 3) == 8 */
DEPOT_POOL_MAX_BLOCKS = 8000,
};
/**
* Called if a new block is added to the depot-pool
*/
static void DepotPoolNewBlock(uint start_item)
{
Depot *d;
Depot *depot;
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
* TODO - This is just a temporary stage, this will be removed. */
for (d = GetDepot(start_item); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) d->index = start_item++;
FOR_ALL_DEPOTS_FROM(depot, start_item)
depot->index = start_item++;
}
DEFINE_OLD_POOL(Depot, Depot, DepotPoolNewBlock, NULL)
/* Initialize the town-pool */
MemoryPool _depot_pool = { "Depots", DEPOT_POOL_MAX_BLOCKS, DEPOT_POOL_BLOCK_SIZE_BITS, sizeof(Depot), &DepotPoolNewBlock, 0, 0, NULL };
/**
@@ -36,7 +41,8 @@ Depot *GetDepotByTile(TileIndex tile)
Depot *depot;
FOR_ALL_DEPOTS(depot) {
if (depot->xy == tile) return depot;
if (depot->xy == tile)
return depot;
}
return NULL;
@@ -47,53 +53,63 @@ Depot *GetDepotByTile(TileIndex tile)
*/
Depot *AllocateDepot(void)
{
Depot *d;
Depot *depot;
/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
* TODO - This is just a temporary stage, this will be removed. */
for (d = GetDepot(0); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) {
if (!IsValidDepot(d)) {
DepotID index = d->index;
FOR_ALL_DEPOTS(depot) {
if (!IsValidDepot(depot)) {
uint index = depot->index;
memset(d, 0, sizeof(Depot));
d->index = index;
memset(depot, 0, sizeof(Depot));
depot->index = index;
return d;
return depot;
}
}
/* Check if we can add a block to the pool */
if (AddBlockToPool(&_Depot_pool)) return AllocateDepot();
if (AddBlockToPool(&_depot_pool))
return AllocateDepot();
return NULL;
}
/**
* Clean up a depot
* Delete a depot
*/
void DestroyDepot(Depot *depot)
void DoDeleteDepot(TileIndex tile)
{
Order order;
Depot *depot;
/* Get the depot */
depot = GetDepotByTile(tile);
/* Clear the tile */
DoClearSquare(depot->xy);
DoClearSquare(tile);
/* Clear the depot */
depot->xy = 0;
/* Clear the depot from all order-lists */
RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, depot->index);
order.type = OT_GOTO_DEPOT;
order.station = depot->index;
DeleteDestinationFromVehicleOrder(order);
/* Delete the depot-window */
DeleteWindowById(WC_VEHICLE_DEPOT, depot->xy);
DeleteWindowById(WC_VEHICLE_DEPOT, tile);
}
void InitializeDepots(void)
void InitializeDepot(void)
{
CleanPool(&_Depot_pool);
AddBlockToPool(&_Depot_pool);
CleanPool(&_depot_pool);
AddBlockToPool(&_depot_pool);
}
static const SaveLoad _depot_desc[] = {
SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, SL_MAX_VERSION),
SLE_VAR(Depot, town_index, SLE_UINT16),
SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
SLE_CONDVAR(Depot, xy, SLE_UINT32, 6, 255),
SLE_VAR(Depot,town_index, SLE_UINT16),
SLE_END()
};
@@ -102,8 +118,10 @@ static void Save_DEPT(void)
Depot *depot;
FOR_ALL_DEPOTS(depot) {
SlSetArrayIndex(depot->index);
SlObject(depot, _depot_desc);
if (IsValidDepot(depot)) {
SlSetArrayIndex(depot->index);
SlObject(depot, _depot_desc);
}
}
}
@@ -114,7 +132,7 @@ static void Load_DEPT(void)
while ((index = SlIterateArray()) != -1) {
Depot *depot;
if (!AddBlockIfNeeded(&_Depot_pool, index))
if (!AddBlockIfNeeded(&_depot_pool, index))
error("Depots: failed loading savegame: too many depots");
depot = GetDepot(index);

122
depot.h
View File

@@ -4,45 +4,42 @@
#define DEPOT_H
/** @file depot.h Header files for depots (not hangars)
* @see depot.c */
* @see depot.c */
#include "direction.h"
#include "oldpool.h"
#include "pool.h"
#include "tile.h"
#include "variables.h"
struct Depot {
TileIndex xy;
TownID town_index;
DepotID index;
uint16 town_index;
uint16 index;
};
DECLARE_OLD_POOL(Depot, Depot, 3, 8000)
extern MemoryPool _depot_pool;
/**
* Check if a depot really exists.
* Get the pointer to the depot with index 'index'
*/
static inline bool IsValidDepot(const Depot *depot)
static inline Depot *GetDepot(uint index)
{
return depot != NULL && depot->xy != 0;
return (Depot*)GetItemFromPool(&_depot_pool, index);
}
static inline bool IsValidDepotID(uint index)
/**
* Get the current size of the DepotPool
*/
static inline uint16 GetDepotPoolSize(void)
{
return index < GetDepotPoolSize() && IsValidDepot(GetDepot(index));
return _depot_pool.total_items;
}
void DestroyDepot(Depot *depot);
static inline void DeleteDepot(Depot *depot)
static inline bool IsDepotIndex(uint index)
{
DestroyDepot(depot);
depot->xy = 0;
return index < GetDepotPoolSize();
}
void ShowDepotWindow(TileIndex tile, byte type);
#define FOR_ALL_DEPOTS_FROM(d, start) for (d = GetDepot(start); d != NULL; d = (d->index + 1U < GetDepotPoolSize()) ? GetDepot(d->index + 1U) : NULL) if (IsValidDepot(d))
#define FOR_ALL_DEPOTS_FROM(d, start) for (d = GetDepot(start); d != NULL; d = (d->index + 1 < GetDepotPoolSize()) ? GetDepot(d->index + 1) : NULL)
#define FOR_ALL_DEPOTS(d) FOR_ALL_DEPOTS_FROM(d, 0)
#define MIN_SERVINT_PERCENT 5
@@ -50,23 +47,36 @@ void ShowDepotWindow(TileIndex tile, byte type);
#define MIN_SERVINT_DAYS 30
#define MAX_SERVINT_DAYS 800
/**
* Get the service interval domain.
/** Get the service interval domain.
* Get the new proposed service interval for the vehicle is indeed, clamped
* within the given bounds. @see MIN_SERVINT_PERCENT ,etc.
* @param index proposed service interval
*/
static inline Date GetServiceIntervalClamped(uint index)
static inline uint16 GetServiceIntervalClamped(uint index)
{
return (_patches.servint_ispercent) ? clamp(index, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : clamp(index, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS);
}
VARDEF TileIndex _last_built_train_depot_tile;
VARDEF TileIndex _last_built_road_depot_tile;
VARDEF TileIndex _last_built_aircraft_depot_tile;
VARDEF TileIndex _last_built_ship_depot_tile;
/**
* Check if a depot really exists.
*/
static inline bool IsValidDepot(const Depot* depot)
{
return depot->xy != 0; /* XXX: Replace by INVALID_TILE someday */
}
/**
* Check if a tile is a depot of the given type.
*/
static inline bool IsTileDepotType(TileIndex tile, TransportType type)
{
switch (type) {
switch(type)
{
case TRANSPORT_RAIL:
return IsTileType(tile, MP_RAILWAY) && (_m[tile].m5 & 0xFC) == 0xC0;
@@ -82,31 +92,57 @@ static inline bool IsTileDepotType(TileIndex tile, TransportType type)
}
}
/**
* Find out if the slope of the tile is suitable to build a depot of given direction
* @param direction The direction in which the depot's exit points. Starts with 0 as NE and goes Clockwise
* @param tileh The slope of the tile in question
* @return true if the construction is possible
* This is checked by the ugly 0x4C >> direction magic, which does the following:
* 0x4C is 0100 1100 and tileh has only bits 0..3 set (steep tiles are ruled out)
* So: for direction (only the significant bits are shown)<p>
* 00 (exit towards NE) we need either bit 2 or 3 set in tileh: 0x4C >> 0 = 1100<p>
* 01 (exit towards SE) we need either bit 1 or 2 set in tileh: 0x4C >> 1 = 0110<p>
* 02 (exit towards SW) we need either bit 0 or 1 set in tileh: 0x4C >> 2 = 0011<p>
* 03 (exit towards NW) we need either bit 0 or 4 set in tileh: 0x4C >> 3 = 1001<p>
* So ((0x4C >> direction) & tileh) determines whether the depot can be built on the current tileh
* Returns the direction the exit of the depot on the given tile is facing.
*/
static inline bool CanBuildDepotByTileh(uint32 direction, Slope tileh)
static inline DiagDirection GetDepotDirection(TileIndex tile, TransportType type)
{
return ((0x4C >> direction) & tileh) != 0;
assert(IsTileDepotType(tile, type));
switch (type)
{
case TRANSPORT_RAIL:
case TRANSPORT_ROAD:
/* Rail and road store a diagonal direction in bits 0 and 1 */
return (DiagDirection)GB(_m[tile].m5, 0, 2);
case TRANSPORT_WATER:
/* Water is stubborn, it stores the directions in a different order. */
switch (GB(_m[tile].m5, 0, 2)) {
case 0: return DIAGDIR_NE;
case 1: return DIAGDIR_SW;
case 2: return DIAGDIR_NW;
case 3: return DIAGDIR_SE;
}
default:
return INVALID_DIAGDIR; /* Not reached */
}
}
Depot *GetDepotByTile(TileIndex tile);
void InitializeDepots(void);
Depot *AllocateDepot(void);
/**
Find out if the slope of the tile is suitable to build a depot of given direction
@param direction The direction in which the depot's exit points. Starts with 0 as NE and goes Clockwise
@param tileh The slope of the tile in question
@return true if the construction is possible
void DeleteDepotHighlightOfVehicle(const Vehicle *v);
This is checked by the ugly 0x4C >> direction magic, which does the following:
0x4C is 0100 1100 and tileh has only bits 0..3 set (steep tiles are ruled out)
So: for direction (only the significant bits are shown)<p>
00 (exit towards NE) we need either bit 2 or 3 set in tileh: 0x4C >> 0 = 1100<p>
01 (exit towards SE) we need either bit 1 or 2 set in tileh: 0x4C >> 1 = 0110<p>
02 (exit towards SW) we need either bit 0 or 1 set in tileh: 0x4C >> 2 = 0011<p>
03 (exit towards NW) we need either bit 0 or 4 set in tileh: 0x4C >> 3 = 1001<p>
So ((0x4C >> p2) & tileh) determines whether the depot can be built on the current tileh
*/
static inline bool CanBuildDepotByTileh(uint32 direction, uint tileh)
{
return (0x4C >> direction) & tileh;
}
Depot *GetDepotByTile(TileIndex tile);
void InitializeDepot(void);
Depot *AllocateDepot(void);
void DoDeleteDepot(TileIndex tile);
#endif /* DEPOT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -3,92 +3,6 @@
#ifndef DIRECTION_H
#define DIRECTION_H
/* Direction as commonly used in v->direction, 8 way. */
typedef enum Direction {
DIR_N = 0,
DIR_NE = 1, /* Northeast, upper right on your monitor */
DIR_E = 2,
DIR_SE = 3,
DIR_S = 4,
DIR_SW = 5,
DIR_W = 6,
DIR_NW = 7,
DIR_END,
INVALID_DIR = 0xFF,
} Direction;
static inline Direction ReverseDir(Direction d)
{
return (Direction)(4 ^ d);
}
typedef enum DirDiff {
DIRDIFF_SAME = 0,
DIRDIFF_45RIGHT = 1,
DIRDIFF_90RIGHT = 2,
DIRDIFF_REVERSE = 4,
DIRDIFF_90LEFT = 6,
DIRDIFF_45LEFT = 7
} DirDiff;
static inline DirDiff DirDifference(Direction d0, Direction d1)
{
return (DirDiff)((d0 + 8 - d1) % 8);
}
static inline DirDiff ChangeDirDiff(DirDiff d, DirDiff delta)
{
return (DirDiff)((d + delta) % 8);
}
static inline Direction ChangeDir(Direction d, DirDiff delta)
{
return (Direction)((d + delta) % 8);
}
/* Direction commonly used as the direction of entering and leaving tiles, 4-way */
typedef enum DiagDirection {
DIAGDIR_NE = 0, /* Northeast, upper right on your monitor */
DIAGDIR_SE = 1,
DIAGDIR_SW = 2,
DIAGDIR_NW = 3,
DIAGDIR_END,
INVALID_DIAGDIR = 0xFF,
} DiagDirection;
static inline DiagDirection ReverseDiagDir(DiagDirection d)
{
return (DiagDirection)(2 ^ d);
}
typedef enum DiagDirDiff {
DIAGDIRDIFF_SAME = 0,
DIAGDIRDIFF_90RIGHT = 1,
DIAGDIRDIFF_REVERSE = 2,
DIAGDIRDIFF_90LEFT = 3
} DiagDirDiff;
static inline DiagDirection ChangeDiagDir(DiagDirection d, DiagDirDiff delta)
{
return (DiagDirection)((d + delta) % 4);
}
static inline DiagDirection DirToDiagDir(Direction dir)
{
return (DiagDirection)(dir >> 1);
}
static inline Direction DiagDirToDir(DiagDirection dir)
{
return (Direction)(dir * 2 + 1);
}
/* the 2 axis */
typedef enum Axis {
@@ -98,13 +12,7 @@ typedef enum Axis {
} Axis;
static inline Axis OtherAxis(Axis a)
{
return (Axis)(a ^ 1);
}
static inline Axis DiagDirToAxis(DiagDirection d)
static inline Axis DiagDirToAxis(uint d)
{
return (Axis)(d & 1);
}
@@ -114,34 +22,9 @@ static inline Axis DiagDirToAxis(DiagDirection d)
* Converts an Axis to a DiagDirection
* Points always in the positive direction, i.e. S[EW]
*/
static inline DiagDirection AxisToDiagDir(Axis a)
static inline uint AxisToDiagDir(Axis a)
{
return (DiagDirection)(2 - a);
return (uint)(2 - a);
}
/**
* Convert an axis and a flag for north/south into a DiagDirection
* @param ns north -> 0, south -> 1
*/
static inline DiagDirection XYNSToDiagDir(Axis xy, uint ns)
{
return (DiagDirection)(xy * 3 ^ ns * 2);
}
static inline bool IsValidDiagDirection(DiagDirection d)
{
return d < DIAGDIR_END;
}
static inline bool IsValidDirection(Direction d)
{
return d < DIR_END;
}
static inline bool IsValidAxis(Axis d)
{
return d < AXIS_END;
}
#endif /* DIRECTION_H */
#endif

View File

@@ -2,10 +2,8 @@
#include "stdafx.h"
#include "openttd.h"
#include "functions.h"
#include "industry_map.h"
#include "station_map.h"
#include "table/strings.h"
#include "functions.h"
#include "map.h"
#include "tile.h"
#include "vehicle.h"
@@ -16,11 +14,10 @@
#include "town.h"
#include "industry.h"
#include "player.h"
#include "airport.h"
#include "airport_movement.h"
#include "sound.h"
#include "variables.h"
#include "table/sprites.h"
#include "date.h"
static void DisasterClearSquare(TileIndex tile)
{
@@ -28,18 +25,13 @@ static void DisasterClearSquare(TileIndex tile)
switch (GetTileType(tile)) {
case MP_RAILWAY:
if (IsHumanPlayer(GetTileOwner(tile)) && !IsRailWaypoint(tile)) {
PlayerID p = _current_player;
_current_player = OWNER_WATER;
DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
_current_player = p;
}
if (IS_HUMAN_PLAYER(GetTileOwner(tile)) && !IsRailWaypoint(tile)) DoClearSquare(tile);
break;
case MP_HOUSE: {
PlayerID p = _current_player;
_current_player = OWNER_NONE;
DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
_current_player = p;
break;
}
@@ -54,24 +46,24 @@ static void DisasterClearSquare(TileIndex tile)
}
}
static const SpriteID _disaster_images_1[] = {0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41};
static const SpriteID _disaster_images_2[] = {0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44};
static const SpriteID _disaster_images_3[] = {0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E};
static const SpriteID _disaster_images_4[] = {0xF46, 0xF46, 0xF47, 0xF47, 0xF48, 0xF48, 0xF49, 0xF49};
static const SpriteID _disaster_images_5[] = {0xF4A, 0xF4A, 0xF4B, 0xF4B, 0xF4C, 0xF4C, 0xF4D, 0xF4D};
static const SpriteID _disaster_images_6[] = {0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50};
static const SpriteID _disaster_images_7[] = {0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51};
static const SpriteID _disaster_images_8[] = {0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52};
static const SpriteID _disaster_images_9[] = {0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E};
static const SpriteID _disaster_images_1[] = {0xF41,0xF41,0xF41,0xF41,0xF41,0xF41,0xF41,0xF41};
static const SpriteID _disaster_images_2[] = {0xF44,0xF44,0xF44,0xF44,0xF44,0xF44,0xF44,0xF44};
static const SpriteID _disaster_images_3[] = {0xF4E,0xF4E,0xF4E,0xF4E,0xF4E,0xF4E,0xF4E,0xF4E};
static const SpriteID _disaster_images_4[] = {0xF46,0xF46,0xF47,0xF47,0xF48,0xF48,0xF49,0xF49};
static const SpriteID _disaster_images_5[] = {0xF4A,0xF4A,0xF4B,0xF4B,0xF4C,0xF4C,0xF4D,0xF4D};
static const SpriteID _disaster_images_6[] = {0xF50,0xF50,0xF50,0xF50,0xF50,0xF50,0xF50,0xF50};
static const SpriteID _disaster_images_7[] = {0xF51,0xF51,0xF51,0xF51,0xF51,0xF51,0xF51,0xF51};
static const SpriteID _disaster_images_8[] = {0xF52,0xF52,0xF52,0xF52,0xF52,0xF52,0xF52,0xF52};
static const SpriteID _disaster_images_9[] = {0xF3E,0xF3E,0xF3E,0xF3E,0xF3E,0xF3E,0xF3E,0xF3E};
static const SpriteID * const _disaster_images[] = {
_disaster_images_1, _disaster_images_1,
_disaster_images_2, _disaster_images_2,
_disaster_images_3, _disaster_images_3,
_disaster_images_8, _disaster_images_8, _disaster_images_9,
_disaster_images_6, _disaster_images_6,
_disaster_images_7, _disaster_images_7,
_disaster_images_4, _disaster_images_5,
_disaster_images_1,_disaster_images_1,
_disaster_images_2,_disaster_images_2,
_disaster_images_3,_disaster_images_3,
_disaster_images_8,_disaster_images_8,_disaster_images_9,
_disaster_images_6,_disaster_images_6,
_disaster_images_7,_disaster_images_7,
_disaster_images_4,_disaster_images_5,
};
static void DisasterVehicleUpdateImage(Vehicle *v)
@@ -82,7 +74,7 @@ static void DisasterVehicleUpdateImage(Vehicle *v)
v->cur_image = img;
}
static void InitializeDisasterVehicle(Vehicle* v, int x, int y, byte z, Direction direction, byte subtype)
static void InitializeDisasterVehicle(Vehicle *v, int x, int y, byte z, byte direction, byte subtype)
{
v->type = VEH_Disaster;
v->x_pos = x;
@@ -101,7 +93,7 @@ static void InitializeDisasterVehicle(Vehicle* v, int x, int y, byte z, Directio
v->u.disaster.image_override = 0;
v->current_order.type = OT_NOTHING;
v->current_order.flags = 0;
v->current_order.dest = 0;
v->current_order.station = 0;
DisasterVehicleUpdateImage(v);
VehiclePositionChanged(v);
@@ -117,6 +109,7 @@ static void DeleteDisasterVeh(Vehicle *v)
static void SetDisasterVehiclePos(Vehicle *v, int x, int y, byte z)
{
Vehicle *u;
int yt;
BeginVehicleMove(v);
v->x_pos = x;
@@ -129,14 +122,11 @@ static void SetDisasterVehiclePos(Vehicle *v, int x, int y, byte z)
EndVehicleMove(v);
if ( (u=v->next) != NULL) {
int safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
int safe_y = clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
BeginVehicleMove(u);
u->x_pos = x;
u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0) >> 3);
safe_y = clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
u->z_pos = GetSlopeZ(safe_x, safe_y);
u->y_pos = yt = y - 1 - (max(z - GetSlopeZ(x, y-1), 0) >> 3);
u->z_pos = GetSlopeZ(x,yt);
u->direction = v->direction;
DisasterVehicleUpdateImage(u);
@@ -165,7 +155,7 @@ static void DisasterTick_Zeppeliner(Vehicle *v)
++v->tick_counter;
if (v->current_order.dest < 2) {
if (v->current_order.station < 2) {
if (v->tick_counter&1)
return;
@@ -173,38 +163,38 @@ static void DisasterTick_Zeppeliner(Vehicle *v)
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
if (v->current_order.dest == 1) {
if (v->current_order.station == 1) {
if (++v->age == 38) {
v->current_order.dest = 2;
v->current_order.station = 2;
v->age = 0;
}
if ((v->tick_counter&7)==0) {
CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
}
} else if (v->current_order.dest == 0) {
} else if (v->current_order.station == 0) {
tile = v->tile; /**/
if (IsValidTile(tile) &&
IsTileType(tile, MP_STATION) &&
IsAirport(tile) &&
IsHumanPlayer(GetTileOwner(tile))) {
v->current_order.dest = 1;
IS_BYTE_INSIDE(_m[tile].m5, 8, 0x43) &&
IS_HUMAN_PLAYER(GetTileOwner(tile))) {
v->current_order.station = 1;
v->age = 0;
SetDParam(0, GetStationIndex(tile));
SetDParam(0, _m[tile].m2);
AddNewsItem(STR_B000_ZEPPELIN_DISASTER_AT,
NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
v->index,
0);
}
}
if (v->y_pos >= ((int)MapSizeY() + 9) * TILE_SIZE - 1)
if (v->y_pos >= ((int)MapSizeY() + 9) * 16 - 1)
DeleteDisasterVeh(v);
return;
}
if (v->current_order.dest > 2) {
if (v->current_order.station > 2) {
if (++v->age <= 13320)
return;
@@ -212,9 +202,9 @@ static void DisasterTick_Zeppeliner(Vehicle *v)
if (IsValidTile(tile) &&
IsTileType(tile, MP_STATION) &&
IsAirport(tile) &&
IsHumanPlayer(GetTileOwner(tile))) {
st = GetStationByTile(tile);
IS_BYTE_INSIDE(_m[tile].m5, 8, 0x43) &&
IS_HUMAN_PLAYER(GetTileOwner(tile))) {
st = GetStation(_m[tile].m2);
CLRBITS(st->airport_flags, RUNWAY_IN_block);
}
@@ -247,16 +237,17 @@ static void DisasterTick_Zeppeliner(Vehicle *v)
EV_EXPLOSION_SMALL);
}
} else if (v->age == 350) {
v->current_order.dest = 3;
v->current_order.station = 3;
v->age = 0;
}
tile = v->tile;/**/
if (IsValidTile(tile) &&
IsTileType(tile, MP_STATION) &&
IsAirport(tile) &&
IsHumanPlayer(GetTileOwner(tile))) {
st = GetStationByTile(tile);
IS_BYTE_INSIDE(_m[tile].m5, 8, 0x43) &&
IS_HUMAN_PLAYER(GetTileOwner(tile))) {
st = GetStation(_m[tile].m2);
SETBITS(st->airport_flags, RUNWAY_IN_block);
}
}
@@ -272,11 +263,11 @@ static void DisasterTick_UFO(Vehicle *v)
v->u.disaster.image_override = (++v->tick_counter & 8) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
if (v->current_order.dest == 0) {
if (v->current_order.station == 0) {
// fly around randomly
int x = TileX(v->dest_tile) * TILE_SIZE;
int y = TileY(v->dest_tile) * TILE_SIZE;
if (abs(x - v->x_pos) + abs(y - v->y_pos) >= TILE_SIZE) {
int x = TileX(v->dest_tile) * 16;
int y = TileY(v->dest_tile) * 16;
if (abs(x - v->x_pos) + abs(y - v->y_pos) >= 16) {
v->direction = GetDirectionTowards(v, x, y);
GetNewVehiclePos(v, &gp);
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
@@ -286,10 +277,10 @@ static void DisasterTick_UFO(Vehicle *v)
v->dest_tile = RandomTile();
return;
}
v->current_order.dest = 1;
v->current_order.station = 1;
FOR_ALL_VEHICLES(u) {
if (u->type == VEH_Road && IsHumanPlayer(u->owner)) {
if (u->type == VEH_Road && IS_HUMAN_PLAYER(u->owner)) {
v->dest_tile = u->index;
v->age = 0;
return;
@@ -307,7 +298,7 @@ static void DisasterTick_UFO(Vehicle *v)
dist = abs(v->x_pos - u->x_pos) + abs(v->y_pos - u->y_pos);
if (dist < TILE_SIZE && !(u->vehstatus&VS_HIDDEN) && u->breakdown_ctr==0) {
if (dist < 16 && !(u->vehstatus&VS_HIDDEN) && u->breakdown_ctr==0) {
u->breakdown_ctr = 3;
u->breakdown_delay = 140;
}
@@ -316,7 +307,7 @@ static void DisasterTick_UFO(Vehicle *v)
GetNewVehiclePos(v, &gp);
z = v->z_pos;
if (dist <= TILE_SIZE && z > u->z_pos) z--;
if (dist <= 16 && z > u->z_pos) z--;
SetDisasterVehiclePos(v, gp.x, gp.y, z);
if (z <= u->z_pos && (u->vehstatus&VS_HIDDEN)==0) {
@@ -346,8 +337,8 @@ static void DestructIndustry(Industry *i)
TileIndex tile;
for (tile = 0; tile != MapSize(); tile++) {
if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
ResetIndustryConstructionStage(tile);
if (IsTileType(tile, MP_INDUSTRY) && _m[tile].m2 == i->index) {
_m[tile].m1 = 0;
MarkTileDirtyByTile(tile);
}
}
@@ -360,7 +351,7 @@ static void DisasterTick_2(Vehicle *v)
v->tick_counter++;
v->u.disaster.image_override =
(v->current_order.dest == 1 && v->tick_counter & 4) ? SPR_F_15_FIRING : 0;
(v->current_order.station == 1 && v->tick_counter & 4) ? SPR_F_15_FIRING : 0;
GetNewVehiclePos(v, &gp);
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
@@ -370,11 +361,11 @@ static void DisasterTick_2(Vehicle *v)
return;
}
if (v->current_order.dest == 2) {
if (v->current_order.station == 2) {
if (!(v->tick_counter&3)) {
Industry *i = GetIndustry(v->dest_tile);
int x = TileX(i->xy) * TILE_SIZE;
int y = TileY(i->xy) * TILE_SIZE;
int x = TileX(i->xy) * 16;
int y = TileY(i->xy) * 16;
uint32 r = Random();
CreateEffectVehicleAbove(
@@ -384,13 +375,13 @@ static void DisasterTick_2(Vehicle *v)
EV_EXPLOSION_SMALL);
if (++v->age >= 55)
v->current_order.dest = 3;
v->current_order.station = 3;
}
} else if (v->current_order.dest == 1) {
} else if (v->current_order.station == 1) {
if (++v->age == 112) {
Industry *i;
v->current_order.dest = 2;
v->current_order.station = 2;
v->age = 0;
i = GetIndustry(v->dest_tile);
@@ -400,26 +391,25 @@ static void DisasterTick_2(Vehicle *v)
AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy, 0);
SndPlayTileFx(SND_12_EXPLOSION, i->xy);
}
} else if (v->current_order.dest == 0) {
} else if (v->current_order.station == 0) {
int x,y;
TileIndex tile;
uint ind;
int ind;
x = v->x_pos - 15 * TILE_SIZE;
x = v->x_pos - 15*16;
y = v->y_pos;
if ( (uint)x > MapMaxX() * TILE_SIZE - 1)
if ( (uint)x > MapMaxX() * 16-1)
return;
tile = TileVirtXY(x, y);
if (!IsTileType(tile, MP_INDUSTRY))
return;
ind = GetIndustryIndex(tile);
v->dest_tile = ind;
v->dest_tile = ind = _m[tile].m2;
if (GetIndustry(ind)->type == IT_OIL_REFINERY) {
v->current_order.dest = 1;
v->current_order.station = 1;
v->age = 0;
}
}
@@ -432,21 +422,21 @@ static void DisasterTick_3(Vehicle *v)
v->tick_counter++;
v->u.disaster.image_override =
(v->current_order.dest == 1 && v->tick_counter & 4) ? SPR_AH_64A_FIRING : 0;
(v->current_order.station == 1 && v->tick_counter & 4) ? SPR_AH_64A_FIRING : 0;
GetNewVehiclePos(v, &gp);
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
if (gp.x > (int)MapSizeX() * 16 + 9*16 - 1) {
DeleteDisasterVeh(v);
return;
}
if (v->current_order.dest == 2) {
if (v->current_order.station == 2) {
if (!(v->tick_counter&3)) {
Industry *i = GetIndustry(v->dest_tile);
int x = TileX(i->xy) * TILE_SIZE;
int y = TileY(i->xy) * TILE_SIZE;
int x = TileX(i->xy) * 16;
int y = TileY(i->xy) * 16;
uint32 r = Random();
CreateEffectVehicleAbove(
@@ -456,13 +446,13 @@ static void DisasterTick_3(Vehicle *v)
EV_EXPLOSION_SMALL);
if (++v->age >= 55)
v->current_order.dest = 3;
v->current_order.station = 3;
}
} else if (v->current_order.dest == 1) {
} else if (v->current_order.station == 1) {
if (++v->age == 112) {
Industry *i;
v->current_order.dest = 2;
v->current_order.station = 2;
v->age = 0;
i = GetIndustry(v->dest_tile);
@@ -472,26 +462,25 @@ static void DisasterTick_3(Vehicle *v)
AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy, 0);
SndPlayTileFx(SND_12_EXPLOSION, i->xy);
}
} else if (v->current_order.dest == 0) {
} else if (v->current_order.station == 0) {
int x,y;
TileIndex tile;
uint ind;
int ind;
x = v->x_pos + (15 * TILE_SIZE);
x = v->x_pos - 15*16;
y = v->y_pos;
if ( (uint)x > MapMaxX() * TILE_SIZE - 1)
if ( (uint)x > MapMaxX() * 16-1)
return;
tile = TileVirtXY(x, y);
if (!IsTileType(tile, MP_INDUSTRY))
return;
ind = GetIndustryIndex(tile);
v->dest_tile = ind;
v->dest_tile = ind = _m[tile].m2;
if (GetIndustry(ind)->type == IT_FACTORY) {
v->current_order.dest = 1;
v->current_order.station = 1;
v->age = 0;
}
}
@@ -523,9 +512,9 @@ static void DisasterTick_4(Vehicle *v)
v->tick_counter++;
if (v->current_order.dest == 1) {
int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
if (v->current_order.station == 1) {
int x = TileX(v->dest_tile) * 16 + 8;
int y = TileY(v->dest_tile) * 16 + 8;
if (abs(v->x_pos - x) + abs(v->y_pos - y) >= 8) {
v->direction = GetDirectionTowards(v, x, y);
@@ -540,11 +529,11 @@ static void DisasterTick_4(Vehicle *v)
return;
}
v->current_order.dest = 2;
v->current_order.station = 2;
FOR_ALL_VEHICLES(u) {
if (u->type == VEH_Train || u->type == VEH_Road) {
if (abs(u->x_pos - v->x_pos) + abs(u->y_pos - v->y_pos) <= 12 * TILE_SIZE) {
if (abs(u->x_pos - v->x_pos) + abs(u->y_pos - v->y_pos) <= 12*16) {
u->breakdown_ctr = 5;
u->breakdown_delay = 0xF0;
}
@@ -564,7 +553,7 @@ static void DisasterTick_4(Vehicle *v)
return;
}
InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, 11);
InitializeDisasterVehicle(u, -6*16, v->y_pos, 135, 5, 11);
u->u.disaster.unk2 = v->index;
w = ForceAllocateSpecialVehicle();
@@ -572,13 +561,13 @@ static void DisasterTick_4(Vehicle *v)
return;
u->next = w;
InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, 12);
w->vehstatus |= VS_SHADOW;
} else if (v->current_order.dest < 1) {
InitializeDisasterVehicle(w, -6*16, v->y_pos, 0, 5, 12);
w->vehstatus |= VS_DISASTER;
} else if (v->current_order.station < 1) {
int x = TileX(v->dest_tile) * TILE_SIZE;
int y = TileY(v->dest_tile) * TILE_SIZE;
if (abs(x - v->x_pos) + abs(y - v->y_pos) >= TILE_SIZE) {
int x = TileX(v->dest_tile) * 16;
int y = TileY(v->dest_tile) * 16;
if (abs(x - v->x_pos) + abs(y - v->y_pos) >= 16) {
v->direction = GetDirectionTowards(v, x, y);
GetNewVehiclePos(v, &gp);
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
@@ -589,22 +578,19 @@ static void DisasterTick_4(Vehicle *v)
v->dest_tile = RandomTile();
return;
}
v->current_order.dest = 1;
v->current_order.station = 1;
tile_org = tile = RandomTile();
do {
if (IsTileType(tile, MP_RAILWAY) &&
IsPlainRailTile(tile) &&
IsHumanPlayer(GetTileOwner(tile))) {
(_m[tile].m5 & ~3) != 0xC0 && IS_HUMAN_PLAYER(GetTileOwner(tile)))
break;
}
tile = TILE_MASK(tile+1);
} while (tile != tile_org);
v->dest_tile = tile;
v->age = 0;
} else {
} else
return;
}
}
// The plane which will shoot down the UFO
@@ -619,23 +605,23 @@ static void DisasterTick_4b(Vehicle *v)
GetNewVehiclePos(v, &gp);
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
if (gp.x > (int)MapSizeX() * 16 + 9*16 - 1) {
DeleteDisasterVeh(v);
return;
}
if (v->current_order.dest == 0) {
if (v->current_order.station == 0) {
u = GetVehicle(v->u.disaster.unk2);
if (abs(v->x_pos - u->x_pos) > TILE_SIZE)
if (abs(v->x_pos - u->x_pos) > 16)
return;
v->current_order.dest = 1;
v->current_order.station = 1;
CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
SndPlayVehicleFx(SND_12_EXPLOSION, u);
DeleteDisasterVeh(u);
for (i = 0; i != 80; i++) {
for(i=0; i!=80; i++) {
uint32 r = Random();
CreateEffectVehicleAbove(
GB(r, 0, 6) + v->x_pos - 32,
@@ -668,18 +654,19 @@ static void DisasterTick_5_and_6(Vehicle *v)
return;
}
if (!(v->tick_counter & 1)) return;
if (!(v->tick_counter&1))
return;
tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
tile = v->tile + TileOffsByDir(v->direction >> 1);
if (IsValidTile(tile) &&
(r=GetTileTrackStatus(tile,TRANSPORT_WATER),(byte)(r|(r >> 8)) == 0x3F) &&
(r=GetTileTrackStatus(tile,TRANSPORT_WATER),(byte)(r+(r >> 8)) == 0x3F) &&
!CHANCE16(1,90)) {
GetNewVehiclePos(v, &gp);
SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
return;
}
v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
v->direction = (v->direction + (GB(Random(), 0, 1) ? 2 : -2)) & 7;
}
@@ -718,28 +705,30 @@ static void Disaster0_Init(void)
Station *st;
int x;
if (v == NULL) return;
if (v == NULL)
return;
/* Pick a random place, unless we find a small airport */
x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
/* Pick a random place, unless we find
a small airport */
x = TileX(Random()) * 16 + 8;
FOR_ALL_STATIONS(st) {
if (st->airport_tile != 0 &&
if (st->xy && st->airport_tile != 0 &&
st->airport_type <= 1 &&
IsHumanPlayer(st->owner)) {
x = (TileX(st->xy) + 2) * TILE_SIZE;
IS_HUMAN_PLAYER(st->owner)) {
x = (TileX(st->xy) + 2) * 16;
break;
}
}
InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, 0);
InitializeDisasterVehicle(v, x, 0, 135, 3, 0);
// Allocate shadow too?
u = ForceAllocateSpecialVehicle();
if (u != NULL) {
v->next = u;
InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, 1);
u->vehstatus |= VS_SHADOW;
InitializeDisasterVehicle(u, x, 0, 0, 3, 1);
u->vehstatus |= VS_DISASTER;
}
}
@@ -748,11 +737,12 @@ static void Disaster1_Init(void)
Vehicle *v = ForceAllocateSpecialVehicle(), *u;
int x;
if (v == NULL) return;
if (v == NULL)
return;
x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
x = TileX(Random()) * 16 + 8;
InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, 2);
InitializeDisasterVehicle(v, x, 0, 135, 3, 2);
v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
v->age = 0;
@@ -760,8 +750,8 @@ static void Disaster1_Init(void)
u = ForceAllocateSpecialVehicle();
if (u != NULL) {
v->next = u;
InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, 3);
u->vehstatus |= VS_SHADOW;
InitializeDisasterVehicle(u,x,0,0,3,3);
u->vehstatus |= VS_DISASTER;
}
}
@@ -774,27 +764,30 @@ static void Disaster2_Init(void)
found = NULL;
FOR_ALL_INDUSTRIES(i) {
if (i->type == IT_OIL_REFINERY &&
(found == NULL || CHANCE16(1, 2))) {
if (i->xy != 0 &&
i->type == IT_OIL_REFINERY &&
(found==NULL || CHANCE16(1,2))) {
found = i;
}
}
if (found == NULL) return;
if (found == NULL)
return;
v = ForceAllocateSpecialVehicle();
if (v == NULL) return;
if (v == NULL)
return;
x = (MapSizeX() + 9) * TILE_SIZE - 1;
y = TileY(found->xy) * TILE_SIZE + 37;
x = (MapSizeX() + 9) * 16 - 1;
y = TileY(found->xy) * 16 + 37;
InitializeDisasterVehicle(v, x, y, 135, DIR_NE, 4);
InitializeDisasterVehicle(v,x,y, 135,1,4);
u = ForceAllocateSpecialVehicle();
if (u != NULL) {
v->next = u;
InitializeDisasterVehicle(u, x, y, 0, DIR_SE, 5);
u->vehstatus |= VS_SHADOW;
InitializeDisasterVehicle(u,x,y,0,3,5);
u->vehstatus |= VS_DISASTER;
}
}
@@ -807,32 +800,35 @@ static void Disaster3_Init(void)
found = NULL;
FOR_ALL_INDUSTRIES(i) {
if (i->type == IT_FACTORY &&
if (i->xy != 0 &&
i->type == IT_FACTORY &&
(found==NULL || CHANCE16(1,2))) {
found = i;
}
}
if (found == NULL) return;
if (found == NULL)
return;
v = ForceAllocateSpecialVehicle();
if (v == NULL) return;
if (v == NULL)
return;
x = -16 * TILE_SIZE;
y = TileY(found->xy) * TILE_SIZE + 37;
x = -16 * 16;
y = TileY(found->xy) * 16 + 37;
InitializeDisasterVehicle(v, x, y, 135, DIR_SW, 6);
InitializeDisasterVehicle(v,x,y, 135,5,6);
u = ForceAllocateSpecialVehicle();
if (u != NULL) {
v->next = u;
InitializeDisasterVehicle(u, x, y, 0, DIR_SW, 7);
u->vehstatus |= VS_SHADOW;
InitializeDisasterVehicle(u,x,y,0,5,7);
u->vehstatus |= VS_DISASTER;
w = ForceAllocateSpecialVehicle();
if (w != NULL) {
u->next = w;
InitializeDisasterVehicle(w, x, y, 140, DIR_SW, 8);
InitializeDisasterVehicle(w,x,y,140,5,8);
}
}
}
@@ -844,10 +840,10 @@ static void Disaster4_Init(void)
if (v == NULL) return;
x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
x = TileX(Random()) * 16 + 8;
y = MapMaxX() * TILE_SIZE - 1;
InitializeDisasterVehicle(v, x, y, 135, DIR_NW, 9);
y = MapMaxX() * 16 - 1;
InitializeDisasterVehicle(v, x, y, 135, 7, 9);
v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
v->age = 0;
@@ -855,8 +851,8 @@ static void Disaster4_Init(void)
u = ForceAllocateSpecialVehicle();
if (u != NULL) {
v->next = u;
InitializeDisasterVehicle(u, x, y, 0, DIR_NW, 10);
u->vehstatus |= VS_SHADOW;
InitializeDisasterVehicle(u,x,y,0,7,10);
u->vehstatus |= VS_DISASTER;
}
}
@@ -865,22 +861,18 @@ static void Disaster5_Init(void)
{
Vehicle *v = ForceAllocateSpecialVehicle();
int x,y;
Direction dir;
byte dir;
uint32 r;
if (v == NULL) return;
r = Random();
x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
x = TileX(r) * 16 + 8;
if (r & 0x80000000) {
y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
dir = DIR_NW;
} else {
y = TILE_SIZE / 2;
dir = DIR_SE;
}
InitializeDisasterVehicle(v, x, y, 0, dir, 13);
y = 8;
dir = 3;
if (r & 0x80000000) { y = MapMaxX() * 16 - 8 - 1; dir = 7; }
InitializeDisasterVehicle(v, x, y, 0, dir,13);
v->age = 0;
}
@@ -889,42 +881,38 @@ static void Disaster6_Init(void)
{
Vehicle *v = ForceAllocateSpecialVehicle();
int x,y;
Direction dir;
byte dir;
uint32 r;
if (v == NULL) return;
r = Random();
x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
x = TileX(r) * 16 + 8;
if (r & 0x80000000) {
y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
dir = DIR_NW;
} else {
y = TILE_SIZE / 2;
dir = DIR_SE;
}
InitializeDisasterVehicle(v, x, y, 0, dir, 14);
y = 8;
dir = 3;
if (r & 0x80000000) { y = MapMaxX() * 16 - 8 - 1; dir = 7; }
InitializeDisasterVehicle(v, x, y, 0, dir,14);
v->age = 0;
}
static void Disaster7_Init(void)
{
int index = GB(Random(), 0, 4);
Industry *i;
uint m;
for (m = 0; m < 15; m++) {
const Industry* i;
FOR_ALL_INDUSTRIES(i) {
if (i->type == IT_COAL_MINE && --index < 0) {
if (i->xy != 0 && i->type == IT_COAL_MINE && --index < 0) {
SetDParam(0, i->town->index);
AddNewsItem(STR_B005_COAL_MINE_SUBSIDENCE_LEAVES,
NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy + TileDiffXY(1, 1), 0);
{
TileIndex tile = i->xy;
TileIndexDiff step = TileOffsByDiagDir(GB(Random(), 0, 2));
TileIndexDiff step = TileOffsByDir(GB(Random(), 0, 2));
uint n;
for (n = 0; n < 30; n++) {
@@ -949,30 +937,33 @@ static DisasterInitProc * const _disaster_initprocs[] = {
Disaster7_Init,
};
#define MK(a, b) { (a) - MAX_YEAR_BEGIN_REAL, (b) - MAX_YEAR_BEGIN_REAL }
static const struct {
Year min;
Year max;
byte min;
byte max;
} _dis_years[] = {
{ 1930, 1955 },
{ 1940, 1970 },
{ 1960, 1990 },
{ 1970, 2000 },
{ 2000, 2100 },
{ 1940, 1965 },
{ 1975, 2010 },
{ 1950, 1985 }
MK(1930, 1955),
MK(1940, 1970),
MK(1960, 1990),
MK(1970, 2000),
MK(2000, 2100),
MK(1940, 1965),
MK(1975, 2010),
MK(1950, 1985)
};
#undef MK
static void DoDisaster(void)
{
byte buf[lengthof(_dis_years)];
byte year = _cur_year;
uint i;
uint j;
j = 0;
for (i = 0; i != lengthof(_dis_years); i++) {
if (_cur_year >= _dis_years[i].min && _cur_year < _dis_years[i].max) buf[j++] = i;
if (year >= _dis_years[i].min && year < _dis_years[i].max) buf[j++] = i;
}
if (j == 0) return;

View File

@@ -18,7 +18,7 @@
static void ShowBuildDockStationPicker(void);
static void ShowBuildDocksDepotPicker(void);
static Axis _ship_depot_direction;
static byte _ship_depot_direction;
void CcBuildDocks(bool success, TileIndex tile, uint32 p1, uint32 p2)
{
@@ -65,44 +65,35 @@ static void PlaceDocks_BuildLock(TileIndex tile)
}
enum {
DTW_CANAL = 3,
DTW_LOCK = 4,
DTW_DEMOLISH = 6,
DTW_DEPOT = 7,
DTW_STATION = 8,
DTW_BUOY = 9
};
static void BuildDocksClick_Canal(Window *w)
{
HandlePlacePushButton(w, DTW_CANAL, SPR_CURSOR_CANAL, 1, PlaceDocks_BuildCanal);
HandlePlacePushButton(w, 3, SPR_CURSOR_CANAL, 1, PlaceDocks_BuildCanal);
}
static void BuildDocksClick_Lock(Window *w)
{
HandlePlacePushButton(w, DTW_LOCK, SPR_CURSOR_LOCK, 1, PlaceDocks_BuildLock);
HandlePlacePushButton(w, 4, SPR_CURSOR_LOCK, 1, PlaceDocks_BuildLock);
}
static void BuildDocksClick_Demolish(Window *w)
{
HandlePlacePushButton(w, DTW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceDocks_DemolishArea);
HandlePlacePushButton(w, 6, ANIMCURSOR_DEMOLISH, 1, PlaceDocks_DemolishArea);
}
static void BuildDocksClick_Depot(Window *w)
{
if (HandlePlacePushButton(w, DTW_DEPOT, SPR_CURSOR_SHIP_DEPOT, 1, PlaceDocks_Depot)) ShowBuildDocksDepotPicker();
if (HandlePlacePushButton(w, 7, SPR_CURSOR_SHIP_DEPOT, 1, PlaceDocks_Depot)) ShowBuildDocksDepotPicker();
}
static void BuildDocksClick_Dock(Window *w)
{
if (HandlePlacePushButton(w, DTW_STATION, SPR_CURSOR_DOCK, 3, PlaceDocks_Dock)) ShowBuildDockStationPicker();
if (HandlePlacePushButton(w, 8, SPR_CURSOR_DOCK, 3, PlaceDocks_Dock)) ShowBuildDockStationPicker();
}
static void BuildDocksClick_Buoy(Window *w)
{
HandlePlacePushButton(w, DTW_BUOY, SPR_CURSOR_BOUY, 1, PlaceDocks_Buoy);
HandlePlacePushButton(w, 9, SPR_CURSOR_BOUY, 1, PlaceDocks_Buoy);
}
static void BuildDocksClick_Landscaping(Window *w)
@@ -124,17 +115,17 @@ static OnButtonClick * const _build_docks_button_proc[] = {
static void BuildDocksToolbWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
switch(e->event) {
case WE_PAINT:
DrawWindowWidgets(w);
break;
case WE_CLICK:
if (e->we.click.widget - 3 >= 0 && e->we.click.widget != 5) _build_docks_button_proc[e->we.click.widget - 3](w);
if (e->click.widget - 3 >= 0 && e->click.widget != 5) _build_docks_button_proc[e->click.widget - 3](w);
break;
case WE_KEYPRESS:
switch (e->we.keypress.keycode) {
switch (e->keypress.keycode) {
case '1': BuildDocksClick_Canal(w); break;
case '2': BuildDocksClick_Lock(w); break;
case '3': BuildDocksClick_Demolish(w); break;
@@ -147,26 +138,27 @@ static void BuildDocksToolbWndProc(Window *w, WindowEvent *e)
break;
case WE_PLACE_OBJ:
_place_proc(e->we.place.tile);
_place_proc(e->place.tile);
break;
case WE_PLACE_DRAG: {
VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata);
VpSelectTilesWithMethod(e->place.pt.x, e->place.pt.y, e->place.userdata);
return;
}
case WE_PLACE_MOUSEUP:
if (e->we.place.pt.x != -1) {
if ((e->we.place.userdata & 0xF) == VPM_X_AND_Y) { // dragged actions
if (e->click.pt.x != -1) {
if ((e->place.userdata & 0xF) == VPM_X_AND_Y) { // dragged actions
GUIPlaceProcDragXY(e);
} else if (e->we.place.userdata == VPM_X_OR_Y) {
DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcBuildCanal, CMD_BUILD_CANAL | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_CANALS));
} else if (e->place.userdata == VPM_X_OR_Y) {
DoCommandP(e->place.tile, e->place.starttile, 0, CcBuildCanal, CMD_BUILD_CANAL | CMD_AUTO | CMD_MSG(STR_CANT_BUILD_CANALS));
}
}
break;
case WE_ABORT_PLACE_OBJ:
RaiseWindowButtons(w);
UnclickWindowButtons(w);
SetWindowDirty(w);
w = FindWindowById(WC_BUILD_STATION, 0);
if (w != NULL) WP(w,def_d).close = true;
@@ -179,13 +171,12 @@ static void BuildDocksToolbWndProc(Window *w, WindowEvent *e)
TileIndex tile_from;
TileIndex tile_to;
tile_from = tile_to = e->we.place.tile;
tile_from = tile_to = e->place.tile;
switch (GetTileSlope(tile_from, NULL)) {
case SLOPE_SW: tile_to += TileDiffXY(-1, 0); break;
case SLOPE_SE: tile_to += TileDiffXY( 0, -1); break;
case SLOPE_NW: tile_to += TileDiffXY( 0, 1); break;
case SLOPE_NE: tile_to += TileDiffXY( 1, 0); break;
default: break;
case 3: tile_to += TileDiffXY(-1, 0); break;
case 6: tile_to += TileDiffXY( 0, -1); break;
case 9: tile_to += TileDiffXY( 0, 1); break;
case 12: tile_to += TileDiffXY( 1, 0); break;
}
VpSetPresizeRange(tile_from, tile_to);
} break;
@@ -197,25 +188,25 @@ static void BuildDocksToolbWndProc(Window *w, WindowEvent *e)
}
static const Widget _build_docks_toolb_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 145, 0, 13, STR_9801_DOCK_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_STICKYBOX, RESIZE_NONE, 7, 146, 157, 0, 13, 0x0, STR_STICKY_BUTTON},
{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 21, 14, 35, SPR_IMG_BUILD_CANAL, STR_BUILD_CANALS_TIP},
{ WWT_IMGBTN, RESIZE_NONE, 7, 22, 43, 14, 35, SPR_IMG_BUILD_LOCK, STR_BUILD_LOCKS_TIP},
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 145, 0, 13, STR_9801_DOCK_CONSTRUCTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_STICKYBOX, RESIZE_NONE, 7, 146, 157, 0, 13, 0x0, STR_STICKY_BUTTON},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 21, 14, 35, SPR_IMG_BUILD_CANAL, STR_BUILD_CANALS_TIP},
{ WWT_PANEL, RESIZE_NONE, 7, 22, 43, 14, 35, SPR_IMG_BUILD_LOCK, STR_BUILD_LOCKS_TIP},
{ WWT_PANEL, RESIZE_NONE, 7, 44, 47, 14, 35, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 7, 44, 47, 14, 35, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 7, 48, 69, 14, 35, SPR_IMG_DYNAMITE, STR_018D_DEMOLISH_BUILDINGS_ETC},
{ WWT_IMGBTN, RESIZE_NONE, 7, 70, 91, 14, 35, SPR_IMG_SHIP_DEPOT, STR_981E_BUILD_SHIP_DEPOT_FOR_BUILDING},
{ WWT_IMGBTN, RESIZE_NONE, 7, 92, 113, 14, 35, SPR_IMG_SHIP_DOCK, STR_981D_BUILD_SHIP_DOCK},
{ WWT_IMGBTN, RESIZE_NONE, 7, 114, 135, 14, 35, SPR_IMG_BOUY, STR_9834_POSITION_BUOY_WHICH_CAN},
{ WWT_IMGBTN, RESIZE_NONE, 7, 136, 157, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP},
{ WWT_PANEL, RESIZE_NONE, 7, 48, 69, 14, 35, 703, STR_018D_DEMOLISH_BUILDINGS_ETC},
{ WWT_PANEL, RESIZE_NONE, 7, 70, 91, 14, 35, 748, STR_981E_BUILD_SHIP_DEPOT_FOR_BUILDING},
{ WWT_PANEL, RESIZE_NONE, 7, 92, 113, 14, 35, 746, STR_981D_BUILD_SHIP_DOCK},
{ WWT_PANEL, RESIZE_NONE, 7, 114, 135, 14, 35, 693, STR_9834_POSITION_BUOY_WHICH_CAN},
{ WWT_PANEL, RESIZE_NONE, 7, 136, 157, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP},
{ WIDGETS_END},
};
static const WindowDesc _build_docks_toolbar_desc = {
WDP_ALIGN_TBR, 22, 158, 36,
WC_BUILD_TOOLBAR, 0,
640-158, 22, 158, 36,
WC_BUILD_TOOLBAR,0,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
_build_docks_toolb_widgets,
BuildDocksToolbWndProc
@@ -223,8 +214,7 @@ static const WindowDesc _build_docks_toolbar_desc = {
void ShowBuildDocksToolbar(void)
{
if (!IsValidPlayer(_current_player)) return;
if (_current_player == OWNER_SPECTATOR) return;
DeleteWindowById(WC_BUILD_TOOLBAR, 0);
AllocateWindowDesc(&_build_docks_toolbar_desc);
if (_patches.link_terraform_toolbar) ShowTerraformToolbar();
@@ -233,29 +223,31 @@ void ShowBuildDocksToolbar(void)
static void BuildDockStationWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
case WE_CREATE: LowerWindowWidget(w, _station_show_coverage + 3); break;
case WE_PAINT: {
int rad;
if (WP(w,def_d).close) return;
w->click_state = (1<<3) << _station_show_coverage;
DrawWindowWidgets(w);
rad = (_patches.modified_catchment) ? CA_DOCK : 4;
if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
if (_station_show_coverage) {
SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
} else {
SetTileSelectBigSize(0, 0, 0, 0);
}
DrawStringCentered(74, 17, STR_3066_COVERAGE_AREA_HIGHLIGHT, 0);
DrawStationCoverageAreaText(4, 50, (uint)-1, rad);
break;
}
case WE_CLICK:
switch (e->we.click.widget) {
switch (e->click.widget) {
case 3:
case 4:
RaiseWindowWidget(w, _station_show_coverage + 3);
_station_show_coverage = e->we.click.widget - 3;
LowerWindowWidget(w, _station_show_coverage + 3);
_station_show_coverage = e->click.widget - 3;
SndPlayFx(SND_15_BEEP);
SetWindowDirty(w);
break;
@@ -278,18 +270,17 @@ static void BuildDockStationWndProc(Window *w, WindowEvent *e)
}
static const Widget _build_dock_station_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3068_DOCK, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 74, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 30, 40, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 30, 40, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA},
{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 17, 30, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL},
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3068_DOCK, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 74, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 30, 40, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 30, 40, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA},
{ WIDGETS_END},
};
static const WindowDesc _build_dock_station_desc = {
WDP_AUTO, WDP_AUTO, 148, 75,
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
-1, -1, 148, 75,
WC_BUILD_STATION,WC_BUILD_TOOLBAR,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
_build_dock_station_widgets,
BuildDockStationWndProc
@@ -302,7 +293,7 @@ static void ShowBuildDockStationPicker(void)
static void UpdateDocksDirection(void)
{
if (_ship_depot_direction != AXIS_X) {
if (_ship_depot_direction != 0) {
SetTileSelectSize(1, 2);
} else {
SetTileSelectSize(2, 1);
@@ -312,9 +303,8 @@ static void UpdateDocksDirection(void)
static void BuildDocksDepotWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
case WE_CREATE: LowerWindowWidget(w, _ship_depot_direction + 3); break;
case WE_PAINT:
w->click_state = (1<<3) << _ship_depot_direction;
DrawWindowWidgets(w);
DrawShipDepotSprite(67, 35, 0);
@@ -324,12 +314,10 @@ static void BuildDocksDepotWndProc(Window *w, WindowEvent *e)
return;
case WE_CLICK: {
switch (e->we.click.widget) {
switch (e->click.widget) {
case 3:
case 4:
RaiseWindowWidget(w, _ship_depot_direction + 3);
_ship_depot_direction = e->we.click.widget - 3;
LowerWindowWidget(w, _ship_depot_direction + 3);
_ship_depot_direction = e->click.widget - 3;
SndPlayFx(SND_15_BEEP);
UpdateDocksDirection();
SetWindowDirty(w);
@@ -348,17 +336,17 @@ static void BuildDocksDepotWndProc(Window *w, WindowEvent *e)
}
static const Widget _build_docks_depot_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 203, 0, 13, STR_3800_SHIP_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 203, 14, 85, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 14, 3, 100, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
{ WWT_PANEL, RESIZE_NONE, 14, 103, 200, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 203, 0, 13, STR_3800_SHIP_DEPOT_ORIENTATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 7, 0, 203, 14, 85, 0x0, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 14, 3, 100, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
{ WWT_PANEL, RESIZE_NONE, 14, 103, 200, 17, 82, 0x0, STR_3803_SELECT_SHIP_DEPOT_ORIENTATION},
{ WIDGETS_END},
};
static const WindowDesc _build_docks_depot_desc = {
WDP_AUTO, WDP_AUTO, 204, 86,
WC_BUILD_DEPOT, WC_BUILD_TOOLBAR,
-1, -1, 204, 86,
WC_BUILD_DEPOT,WC_BUILD_TOOLBAR,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
_build_docks_depot_widgets,
BuildDocksDepotWndProc
@@ -374,5 +362,5 @@ static void ShowBuildDocksDepotPicker(void)
void InitializeDockGui(void)
{
_ship_depot_direction = AXIS_X;
_ship_depot_direction = 0;
}

View File

@@ -1,59 +0,0 @@
STRGEN USAGE
------------
This guide is only interesting for people who want to alter something
themselves without access to WT2 (translator2.openttd.org). Please note that
your compiled language file will only be compatible with the OpenTTD version
you have downloaded english.txt, the master language file, for. While this is
not always true, namely when changes in the code have not touched language
files, your safest bet is to assume this 'limitation'.
As a first step you need to compile strgen. This is as easy as typing
'make strgen'. You can also download a precompiled binary from a release,
nightly, etc.
strgen takes as argument a txt file and translates it to a lng file, allowing
it to be used inside OpenTTD. strgen needs the master language file
english.txt to work. Below are some examples of strgen usage.
EXAMPLES
--------
Example 1:
if you are in the root of your working copy (svn code), you should type
strgen/strgen -s lang lang/english.txt
to compile englist.txt into english.lng. It will be placed in the lang dir
Example 2:
you only have the strgen executable (no working copy) and you want to compile
a txt file in the same directory. You should type
./strgen english.txt
and you will get and english.lng in the same dir
Example 3:
you have strgen somewhere, english.txt in /usr/openttd/lang and you want the
resulting language file to go to /tmp. Use
./strgen -s /usr/openttd/lang -d /tmp english.txt
You can interchange english.txt to whichever language you want to generate a
.lng file for.
STRGEN COMMAND SWITCHES
-----------------------
-v | --version
strgen will tell what svn revision it was last modified
-t | --todo
strgen will add <TODO> to any untranslated/missing strings and use the english
strings while compiling the language file
-w | --warning
strgen will print any missing strings or wrongly translated (bad format)
to standard error output(stderr)
-h | --help | -?
Print out a summarized help message explaining these switches
-s | --source_dir
strgen will search for the master file english.txt in the directory specified
by this switch instead of the current directory
-d | --dest_dir
strgen will put <language>.lng in the directory specified by this switch; if
no dest_dir is given, output is the same as source_dir

View File

@@ -32,3 +32,18 @@ this will need english.txt to be present
-w
strgen will print any missing strings to standard error output(stderr)
this will need english.txt to be present
here are a very useful tool for translators:
http://openttd.rulez.org/
HOWTO compile strgen:
(this should be useless as you can just type make)
Goto the main dir
Compile by typing
gcc strgen/strgen.c -o strgen/strgen -DUNIX
or if you want it to tell the revision too
gcc strgen/strgen.c rev.o -o strgen/strgen -DUNIX -DWITH_REV (this is the one the makefile uses)
you now have a program called strgen in the strgen directory

View File

@@ -2,9 +2,9 @@ Welcome to the manual for OpenTTD. The latest release version at the time of wr
1 Obtaining OpenTTD.
You can obtain built binaries of OpenTTD for the 4 supported platforms - Win32, Linux,-x86, BeOS 5 and MacOS-X from the projects Sourceforge page, at http://sourceforge.net/projects/openttd . For the non-Win32 builds you will need libSDL.so, libpng.so and zlib.so compiled for your platform. Some builds will include these.
You can obtain built binaries of OpenTTD for the 4 supported platforms - Win32, Linux,-x86, BeOS 5 and MacOS-X from the projects Sourceforge page, at http://sourceforge.net/projects/openttd . For the non-Win32 builds you will need libSDL.so, libpng.so and zlib.so compiled for your platform. Some builds will include these.
If you use another platform, such as FreeBSD, which has POSIX file i/o and an SDL port, you should be able to build OpenTTD from its source. This is available in the proejcts Subversion repository at svn://svn.openttd.com . The module name is "trunk".
If you use another platform, such as FreeBSD, which has POSIX file i/o and an SDL port, you should be able to build OpenTTD from its source. This is available in the proejcts Subversion repository at svn://svn.openttd.com . The module name is "trunk".
1.1 Building OpenTTD.
@@ -18,11 +18,12 @@ Use make or gmake to compile OpenTTD. You can adjust Makefile.config to compile
BeOS:
On BeOS, run ./configure and then use jam. There are a variaty of options you can pass to your build tool, these are reported by ./configure.
1.2 Installing OpenTTD.
On Windows, insert your "Transport Tycoon Deluxe for Windows 95" disk. You can use a DOS version, but your graphics will be purple. NB: Even if your version of Transport Tycoon Deluxe ran on Windows 95, it may still be the DOS version. Then run the OpenTTD installer.
On Windows, insert your "Transport Tycoon Deluxe for Windows 95" disk. You can use a DOS version, but your graphics will be purple. NB: Even if your version of Transport Tycoon Deluxe ran on Windows 95, it may still be the DOS version. Then run the OpenTTD installer.
On UNIX platforms; decompress your OpenTTD archive, or otherwise run the installer. You should be left with an OpenTTD directory on your system. In this directory, make a subdirectory called 'data', and into this place the sample.cat file and all the .grf files from the install CD of 'Transport Tycoon Deluxe for Windows 95".
On UNIX platforms; decompress your OpenTTD archive, or otherwise run the installer. You should be left with an OpenTTD directory on your system. In this directory, make a subdirectory called 'data', and into this place the sample.cat file and all the .grf files from the install CD of 'Transport Tycoon Deluxe for Windows 95".
(Alternatively you can use the TTD GRF files from the DOS version: TRG1.GRF, TRGC.GRF, TRGH.GRF, TRGI.GRF, TRGT.GRF. A few minor graphical glitches with the DOS graphics remain. E.g. the autorail button in the rail toolbar doesn't look as nice as with the Windows graphics.)
If you want MIDI music, copy the 'gm' folder from the original game directory/CD to the OpenTTD folder.
@@ -37,7 +38,7 @@ On BeOS and Mac OS-X, just double click the ttd binary in the Tracker/Finder. Yo
1.4 Configuring OpenTTD
OpenTTD's launch menu contains three configuration menus - Difficulty Settings, Configure Patches and Game Settings. Most of these menus can be configured from within a running game as well.
OpenTTD's launch menu contains three configuration menus - Difficulty Settings, Configure Patches and Game Settings. Most of these menus can be configured from within a running game as well.
Difficulty Settings lets you configure settings that affect the difficulty of playing the game. These include when your (computer-controlled) competitors can start building, how many of them there are, and how intelligent they are. You can also control how much the subsidy mutliplier is for subsidised routes, and how stable/volitile the in-game economy is. You can also set how you want the terrain to be configured in a random game.
@@ -51,13 +52,14 @@ This section of the manual is written with the assumption that you already know
2.2 Station Construction
In OpenTTD, you can build rail stations up to seven squares long and with up to seven platforms. You can also have stations spreading across far larger distances, allowing a large rail station to be connected to a large airport, for instance.
In OpenTTD, you can build rail stations up to seven squares long and with up to seven platforms. You can also have stations spreading across far larger distances, allowing a large rail station to be connected to a large airport, for instance.
However, there is an even more noticable difference in rail station construction. You may now add platforms and lenght to a station after it has been built, and you may also add platforms of a different type. Users of TTDPatch will be used to this behaviour. But beyond what TTDPatch has, you can make stations of uneven lenght/width, and even ones with perpendicular tracks. You can also delete single tiles or tracks from a station, by holding down Shift before pressing the station construction button.
2.3 Checkpoint Stations
Checkpoint stations (the small blue item in the rail construction window) are small 1x1 stations. They must be built on top of pre-existing track. They do not accept or produce carge of any kind. They exist solely for use as route points. They become useful when dealing with large networks where trains may attempt to route themselves along undesirable or impossible routes. As an alternative to checkpoint stations, you can also direct trains to visit depots along the way. This has the advantage of also servicing the train and hence the train will rarely to never need to depart from its route to be serviced.
Checkpoint stations (the small blue item in the rail construction window) are small 1x1 stations. They must be built on top of pre-existing track. They do not accept or produce carge of any kind. They exist solely for use as route points. They become useful when dealing with large networks where trains may attempt to route themselves along undesirable or impossible routes. As an alternative to checkpoint stations, you can also direct trains to visit depots along the way. This has the advantage of also servicing the train and hence the train will rarely to never need to depart from its route to be serviced.
2.4 Freeform Rail Laying.
@@ -73,7 +75,7 @@ This allows you to build roads, rails, stations and depots on slopes. It also al
2.7 Long Bridges
OpenTTD allows you to constuct bridges up to 127 squares - half the size of the current map. This means that the crossing of large estuaries, such at the Bristol Channel in the original "West Country 90210" scenario can be acheived with one bridge instead of many bridges with staging points.
OpenTTD allows you to constuct bridges up to 127 squares - half the size of the current map. This means that the crossing of large estuaries, such at the Bristol Channel in the original "West Country 90210" scenario can be acheived with one bridge instead of many bridges with staging points.
2.8 Long trains
@@ -87,7 +89,7 @@ This addition to OpenTTD allows you to see the current speed of any vehicle in t
Virtually any settings - train numbers, start date, what vehicles your competitors can use, etc - can be set in OpenTTD. Just use the Configure Patches menu on the main screen.
2.11 Network Play
2.11 Network Play
See multiplayer.txt for more info.
@@ -97,4 +99,4 @@ This button, at the end of the train construction window, lets you 'recycle' tra
2.13 Canal Building
This button, at the end of the water construction window, lets you build canals and shiplifts across the landscape. These act just like normal water.
This button, at the end of the water construction window, lets you build canals and shiplifts across the landscape. These act just like normal water.

View File

@@ -20,4 +20,4 @@ data (containing the grf files)
GM (optional for music)
scenario (optional pregenerated maps)
The game adds some items by itself when it runs, like a save folder and a setting file
The game adds some items by itself when it runs, like a save folder and a setting file

View File

@@ -0,0 +1,10 @@
There are currently two different downloads for OSX, one is named osx, while the other one is named osx-jaguar
The reason for this is that Apple added more functionality to 10.3 and to make use of that, it will no longer run on OSX 10.2 (codenamed Jaguar). To make the game useable on Jaguar, then a special download is available, but it will miss the bugfixes/features, that relies on newer functionality.
The general download is a universal binary and should work great on all macs using 10.3.9 or newer, no matter what processor it got. You should at all time avoid the Jaguar Build if possible.
Technically the universal binary is a triple binary because it contains code for 3 CPUs and the CPUs in question are PPC (G3+G4), G5 and Intel. The Jaguar build is only optimised for PPC and other (newer) CPUs will not benefit so much from their advanced features. This is another reason to avoid the Jaguar build. It's generally slower on modern CPUs.
Current list of missing features in the Jaguar build:
-Can't save screenshots or savegames if certain chars are in the filename. This mainly applies to European chars and hits German hard as the month Mär (Mar) can't be saved

View File

@@ -0,0 +1,62 @@
Compiling and developing OpenTTD on MandrakeLinux 10.0 Official
A quick guide to get started with OpenTTD development on Linux.
---------------------------------------------------------------
1.) RPMs:
Most packages that are required for development (like gcc) should already be installed on your box. You will require those RPMs additionally:
- libsdl1.2-devel-1.2.7-2mdk
- subversion-1.0.1-1mdk (+ dependencies)
- libsvn_ra_svn1_0-1.0.1-1mdk
2.) Subversion:
To obtain the source code from the subversion server type
$ svn co svn://svn.openttd.com/openttd/trunk openttd
from command line to dump the code into the directory 'openttd'.
To update your working copy to the latest revision use
$ svn update
Don't worry, your version will be merged with the latest version.
The command
$ svn diff > mypatch.diff
creates a patch file (aka diff file) which you can submit to the developers to share your improvements.
You can undo changes to a file with
$svn revert filename
3.) Required data files:
Copy the following files from Transport Tycoon Deluxe to openttd/data/
sample.cat
trg1r.grf
trgcr.grf
trghr.grf
trgir.grf
trgtr.grf
(Alternatively you can use the TTD GRF files from the DOS version: TRG1.GRF, TRGC.GRF, TRGH.GRF, TRGI.GRF, TRGT.GRF. A few minor graphical glitches with the DOS graphics remain. E.g. the autorail button in the rail toolbar doesn't look as nice as with the Windows graphics.)
4.) Compiling and running:
Compile OpenTTD with
$ make
and run it with
$ ./ttd
5.) Playing the soundtrack:
If you want the original TTD music you need to copy the whole /gm/ directory from Windows. Additionally the TiMidity driver is required:
TiMidity++-2.12.0-0.pre1.4mdk (+ dependencies)
To run OpenTTD with music support type
$ ./ttd -m extmidi
X.) Last Update: $Date: 2004-06-01 19:08:09 +0200 (Tue, 01 Jun 2004) $
Written for revision $Rev: 710 $

View File

@@ -67,25 +67,16 @@ BUILDING THE OS/2 VERSION
Compiler
--------
Innotek GCC, an OS/2 port of the popular GCC compiler, was used to build OpenTTD.
See www.innotek.de for more information. You WILL need a reasonably UNIX-like
build environment in order to build OpenTTD successfully - the following link
may help to set one up (although some of the links from that page are broken):
Open Watcom 1.3 was used to build OpenTTD (earlier versions will
NOT work). See http://www.openwatcom.org/ to download it. It may
also be possible to build OpenTTD with GCC: I attempted this
before using Open Watcom, but found the tools available for OS/2
at the time to be a bit more tricky to get working.
http://www.mozilla.org/ports/os2/gccsetup.html
To build, you should, if your environment is set up well enough, be able to just
type `./configure' (or `sh configure' if you're using the OS/2 shell) and `make'.
A note on Open Watcom
---------------------
Open Watcom C/C++ was previously used to build OpenTTD (version 0.4.x and earlier).
However, due to advanced C++ features used in the YAPF portion of OpenTTD 0.5
in particular, the compiler is no longer able to build the game at the moment.
Hopefully one day Open Watcom will be able to catch up and we will be able to build
the game once again (it's easier than getting an OS/2 UNIX-like environment set up
in my opinion!), but until then, OpenTTD 0.5 and later can only be built with GCC.
Due to complexities in my set-up, I actually used the Win32 version
of Open Watcom to initially compile OpenTTD for OS/2. There should
be no reason of course why the OS/2 version cannot be used, and I
have subsequently built OpenTTD successfully this way.
Libraries Required
------------------
@@ -96,23 +87,42 @@ to an IDE project file and built a library. Do not use the makefiles
provided, they are not designed for Watcom (apart from SDL):
- zlib
http://www.zlib.org/
http://www.zlib.org/ - contains a makefile for OS/2, but is out
of date and uses EMX, ignore this
- libpng
http://www.libpng.org/
http://www.libpng.org/ - contains an EMX/gcc makefile, ignore this
- SDL for OS/2
ftp://ftp.netlabs.org/pub/sdl/sdl-1.2.7-src-20051222.zip used for
0.4.7
- Freetype
http://freetype.sourceforge.net/
Currently, there are no pre-built libraries available for GCC. If you manage to get
OpenTTD working on Watcom though (do let us know if this is the case!), pre-built
versions can be downloaded from the Files section at
If you do not wish to build the libraries yourself, pre-built versions
can be downloaded from the Files section at
http://sourceforge.net/projects/openttd/ - see "os2-useful-v1.1.zip".
A Note About Subversion Revision Numbers
----------------------------------------
The project file uses a bit of a hack to find out the SVN revision number and
create an appropriate rev.c file. You'll need the SVN tools in your path
(specifically, "svnversion"). If "svnversion" can't be found, a generic rev.c
with the revision set to "norev000" will be created. To specifically force a
version number, set the environment variable "RELEASE" to the number (eg, "0.3.6")
-before- starting the Open Watcom IDE (which must be launched from the same shell
session). Also, beware, as you WILL cause incompatibilities if you try to
play a multiplayer game with a different version.
Compiling
---------
To compile, open the os/os2/openttd.wpj file in the IDE and first build
the strgen.exe target. This will build the .lng file generator, and will
also attempt to build all the language files (plus the table\strings.h
file which is required for openttd.exe to be built). Once strgen.exe and
the language files are built successfully, you can build the openttd.exe
target.
Contact Information
-------------------
@@ -120,6 +130,4 @@ If you have any questions regarding OS/2 issues, please contact me
(owen@owenrudge.net) and I'll try to help you out. For general OpenTTD
issues, see the Contacting section of readme.txt.
Thanks to Paul Smedley for his help with getting OpenTTD to compile under GCC on OS/2.
- Owen Rudge, 8th January 2007
- Owen Rudge

View File

@@ -1,108 +0,0 @@
Compiling OpenTTD using Microsoft Visual C++
December 28, 2006
--------------------------------------------
PLEASE READ THE ENTIRE DOCUMENT BEFORE DOING ANY ACTUAL CHANGES!!
SUPPORTED MSVC COMPILERS
------------------------
OpenTTD includes projects for MSVC 2003.NET and MSVC 2005.NET. Both will
compile out of the box, providing you have the required libraries/headers;
which ones, see below. There is no support for VS6, you are therefore
strongly encouraged to either upgrade to MSVC 2005 Express (free) or use GCC.
MSVC 2002 probably works as well, but it has not been tested.
1) REQUIRED FILES
-----------------
You might already have some of the files already installed, so check before
downloading; mostly because the DirectX SDK and Platform SDK are about
500MB each.
Download the following files:
* openttd-useful.zip (http://sf.net/project/showfiles.php?group_id=103924&package_id=114307)
* DirectX 8.1 SDK (http://neuron.tuke.sk/~mizanin/eng/Dx81sdk-include-lib.rar) (or alternatively the latest DirectX SDK from Microsoft)
* MS Windows Platform SDK (http://www.microsoft.com/downloads/details.aspx?FamilyId=A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5&displaylang=en)
* afxres.h (http://www-d0.fnal.gov/d0dist/dist/packages/d0ve/devel/windows/AFXRES.H)
...and of course the newest source from svn://svn.openttd.org/trunk
You need an SVN-client to download the source from subversion:
* CLI Subversion (http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91)
* GUI TortoiseSVN (http://tortoisesvn.tigris.org/download.html)
2) INCLUDES AND LIBRARIES
-------------------------
Put the newly downloaded files in the VC lib\ and include\ directories; where
"C:\Program Files\Microsoft Visual Studio 8\VC" is your location of Visual C.
If you are compiling for an x64 system, use the include\ and lib\ directories
from the win64/ folder.
* openttd-useful.zip\include\*
* afxresh.h
to > C:\Program Files\Microsoft Visual Studio 8\VC\Include
* openttd-useful.zip\lib\*
to > C:\Program Files\Microsoft Visual Studio 8\VC\Lib
Custom directories might be recommended, check 2.2)
2.1) INCLUDES AND LIBRARIES - DIRECTX/PLATFORM SDK
--------------------------------------------------
Basically the same procedure as with the useful zip file, providing
you are not using the Microsoft installer. Put the include files in the
include\ directory and the library files to the Lib\ directory.
It is recommended to use custom directories so you don't overwrite any
default header or library files.
2.2) CUSTOM DIRECTORIES
-----------------------
If you have put the above include and/or library files into custom folders,
MSVC will not find them by default. You need to add these paths to VC through:
Tools > Options > Projects and Solutions > VC++ Directories > show directories for
* Include files: Add the DirectX/Platform SDK include dir you've created
* Library files: Add the path to the SDK custom lib dir
NOTE: make sure that the directory for the DirectX SDK is the first one in the
list, above all others, otherwise compilation will most likely fail!!
3) TTD GRAPHICS FILES
---------------------
Copy the following files from Transport Tycoon Deluxe to the data folder
* sample.cat
* trg1r.grf
* trgcr.grf
* trghr.grf
* trgir.grf
* trgtr.grf
4) COMPILING
------------
Open trunk/openttd[_vs80].sln
Set the build mode to 'Release' in
Build > Configuration manager > Active solution configuration > select "Release"
Compile...
If everything works well the binary should be in trunk/Release/openttd.exe
5) EDITING, CHANGING SOURCE CODE
--------------------------------
Set the build mode (back to) 'Debug'
Change the startup project to openttd by right-clicking the 'openttd' project
in the Solution Explorer and selecting 'Set as Startup Project'. The 'openttd'
project should now show up bold instead of 'strgen'.
6) PROBLEMS?
------------
If compilation fails, double-check that you are using the latest SVN (!)
source. If it still doesn't work, check in on IRC (irc://irc.oftc.net/openttd),
to ask about reasons; or just wait. The problem will most likely solve itself
within a few days as the problem is noticed and fixed.
An up-to-date version of this README can be found on the wiki:
http://wiki.openttd.org/index.php/MicrosoftVisualCExpress

View File

@@ -0,0 +1,104 @@
Compiling OpenTTD using Microsoft Visual C++ 6.0
Step 1: Ingredients
Download the following files:
* Openttd-useful.zip (http://sourceforge.net/project/showfiles.php?group_id=103924&amp;package_id=114307&amp;release_id=228633)
* DirectX 8.1 SDK (http://neuron.tuke.sk/~mizanin/eng/Dx81sdk-include-lib.rar) (or alternatively the latest DirectX SDK from Microsoft)
* The February 2003 Microsoft Platform SDK (http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm) (newer SDK's do not work with MSVC6)
* afxres.h (http://www-d0.fnal.gov/d0dist/dist/packages/d0ve/devel/windows/AFXRES.H) (maybe you not need this)
...and of course the newest source from svn://svn.openttd.org/trunk
You have to have a SVN-client to download the source:
* Command line version (Subversion 1.2.3 Win32 binaries) (http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91)
* GUI TortoiseSVN (http://tortoisesvn.tigris.org/download.html)
Step 2: Includes and Libraries
Put the newly downloaded files in the VC lib and include directories (Where "C:\Program Files\Microsoft Visual Studio\VC98" is your local location of VC)
* zconf.h [useful.zip]
* zlib.h [useful.zip]
* png.h [useful.zip]
* pngconf.h [useful.zip]
* afxres.h
in
C:\Program Files\Microsoft Visual Studio\VC98\Include
and
* zlibstat.lib [useful.zip]
* libpng.lib [useful.zip]
in
C:\Program Files\Microsoft Visual Studio\VC98\Lib
Step 3: DirectX SDK
(This should work with the latest DirectX SDK as well.)
There are 2 folder in the compressed file: Include and Lib
Copy all files from Include folder to
C:\Program Files\Microsoft Visual Studio\VC98\Include
and all files from Lib folder to
C:\Program Files\Microsoft Visual Studio\VC98\Lib
You can also make custom directories, which is recommended so you don't overwrite VS6 files, for libraries (.lib) and includes/header files (.h) and add it to the VC paths via:
Tools -> Options -> Directories -> show directories for:
a) include files (the include dir: C:\Program Files\Microsoft Visual Studio\VC98\DirectX 8.1 SDK\include )
b) library files (the lib dir, C:\Program Files\Microsoft Visual Studio\VC98\DirectX 8.1 SDK\lib )
NOTE: make sure that the directory for the DirectX SDK is the first one in the list, above all others, otherwise compilation will most likely fail!!
Step 4: TTD Graphics files
Copy the following files from Transport Tycoon Deluxe to the data folder
* sample.cat
* trg1r.grf
* trgcr.grf
* trghr.grf
* trgir.grf
* trgtr.grf
Step 5: Compiling
Open trunk/openttd.dsw
Build menu > Set active configuration > Select: "openttd - Win32 Release"
Compile...
Now it should work, it worked for me :)
From r1319 you can compile branch/map in Debug mode (by Bociusz)
For compiling branch/cargo-packets you have to add cargo.c and .h to this tree's openttd.dsp
If it's not working, and you checked that you using the newest SVN (!) report to Bociusz on IRC (irc://irc.freenode.net/openttd)
Go ahead and make that patch! Happy Hacking! :)
Originally written by Dribbel
Project file updating by Bociusz

58
docs/console.txt Normal file
View File

@@ -0,0 +1,58 @@
OPENTTD INGAME CONSOLE DOCUMENTATION
====================================
http://wiki.openttd.org/index.php/OpenTTDDevBlackBook
for detailed information
*** WARNING **
This document is out of date
*** WARNING **
HOTKEY: BACKQUOTE (aka tilde, the key left to "1")
COMMANDS:
---------
echo [string]
echoc [color-code] [string]
exit
debug_level [0-9]
dump_vars [filter]
help
list_cmds [filter]
list_vars [filter]
printf [formatstring] [var] [var] [var]....
printfc [color-code] [formatstring] [var] [var] [var]....
quit
random
screenshot ["big"/"no_con"]
VARIABLES:
----------
*con_developer
-> true = console debugging info
-> false = no output
*developer
-> 0 = no output
-> 1 = console error output [like command not found]
-> 2 = console error and debug output
*cursor_rate
-> 1-12 = defines the cursor blink interval
VARIABLE HANDLING:
------------------
developer = 0
developer ++
temp_string = test
temp_string = "my little"
printf "%s world" temp_string
---------------------------------------------------
feel free to add more commands and use this in-game
console for your debugging / enhancements

20
docs/directmusic.txt Normal file
View File

@@ -0,0 +1,20 @@
Notes about DirectMusic driver for Win32
----------------------------------------
If compiling under MinGW32, you require the DirectX 7.0 files for MinGW32, available from
http://alleg.sourceforge.net/files/dx70_mgw.zip.
If compiling under MSVC 6.0, you require the equivalent for MSVC, available from
http://alleg.sourceforge.net/files/dx70_min.zip. MSVC 7.0/7.1 include header files that
are recent enough.
If you do not want to compile the DirectMusic driver, undefine WIN32_ENABLE_DIRECTMUSIC_SUPPORT
in stdafx.h.
Bugs, etc
---------
- The volume control doesn't work properly. I'll fix this soon.
Owen Rudge
14th March 2004

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -40,9 +40,7 @@ OTTD's class-specific periodic tile processing routine is called once every +256
<tr><td nowrap valign=top><tt>03</tt>&nbsp; </td><td align=left>full grass</td></tr>
<tr><td nowrap valign=top><tt>07</tt>&nbsp; </td><td align=left>rough land</td></tr>
<tr><td nowrap valign=top><tt>0B</tt>&nbsp; </td><td align=left>rocks</td></tr>
<tr><td nowrap valign=top><tt>0F</tt>&nbsp; </td><td align=left>fields; type of fields in m3 bits 3..0 (legal values: 0 through 9)<br>
m2: Index into the array of industries (farms), INVALID_INDUSTRY (0xFFFF) if farm has been removed </td>
</tr>
<tr><td nowrap valign=top><tt>0F</tt>&nbsp; </td><td align=left>fields; type of fields in m3 bits 3..0 (legal values: 0 through 9)</td></tr>
<tr><td nowrap valign=top><tt>10</tt>&nbsp; </td><td align=left>1/4 snow</td></tr>
<tr><td nowrap valign=top><tt>11</tt>&nbsp; </td><td align=left>2/4 snow</td></tr>
<tr><td nowrap valign=top><tt>12</tt>&nbsp; </td><td align=left>3/4 snow</td></tr>
@@ -131,7 +129,7 @@ m5 bit 7 clear: railway track
<tr><td nowrap valign=top><tt>B</tt>&nbsp; </td><td align=left>fence on the N side (track in the S corner)</td></tr>
<tr><td nowrap valign=top><tt>C</tt>&nbsp; </td><td align=left>on snow or desert</td></tr>
</table></li>
<li>m3 bits 0..3 = <a name="TrackType">track type</a>: <tt>0</tt> - conventional railway, <tt>1</tt> - electrified railway, <tt>2</tt> - monorail, <tt>3</tt> - maglev
<li>m3 bits 0..3 = <a name="TrackType">track type</a>: <tt>0</tt> - conventional railway, <tt>1</tt> - monorail, <tt>2</tt> - maglev
</ul>
m5 bits 7 and 6 set: railway depot / checkpoints
<ul>
@@ -144,6 +142,7 @@ m5 bits 7 and 6 set: railway depot / checkpoints
<li>m1: <a href="#OwnershipInfo">owner</a> of the depot / checkpoint</li>
<li>m2: For waypoints, index into the array of waypoints.</li>
<li>m3 bits 0..3 = <a href="#TrackType">track type</a></li>
<li>m3 bit 4 = use custom sprite (valid only for the checkpoint)</li>
<li>m4 bits 0..3 = ground type, as per m2 bits 0..3 for railway tiles.</li>
</ul>
</td></tr>
@@ -382,8 +381,8 @@ exit towards: <tt>47</tt> - NE, <tt>48</tt> - SE, <tt>49</tt> - SW, <tt>4A</tt>
<li>m1: <a href="#OwnershipInfo">owner</a> of the station</li>
<li>m2: index into the <a href="#_StationArray">array of stations</a></li>
<li>m3 bits 0..3: <a href="#TrackType">track type</a> for railway stations, must be 0 for all the other stations</li>
<li>m3 bits 4..7: persistent random data for newstations</li>
<li>m4 = custom station id; 0 means standard graphics</li>
<li>m3 bit 4 = use custom sprite (valid only railway stations FOR NOW)</li>
<li>m4 = custom station id</li>
</ul>
</td></tr>

View File

@@ -55,24 +55,15 @@ the array so you can quickly see what is used and what is not.
<td class="bits">7654 3210</td>
</tr>
<tr>
<td rowspan="2">0</td>
<td>0</td>
<td class="caption">ground</td>
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO</span> XXXX</td>
<td class="bits">XXXX XX<span class="free">OO</span></td>
<td class="bits">XXXX XXXX</td>
<td class="bits">XXXX XXXX</td>
</tr>
<tr>
<td class="caption">farmland</td>
<td class="bits">-inherit-</td>
<td class="bits">XXXX XXXX XXXX XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX</td>
<td class="bits">-inherit-</td>
<td class="bits">-inherit-</td>
<td class="bits"><span class="free">OOO</span>X XXXX</td>
</tr>
<tr>
<td rowspan=3>1</td>
<td class="caption">rail</td>
@@ -96,7 +87,7 @@ the array so you can quickly see what is used and what is not.
<td class="caption">waypoint</td>
<td class="bits">-inherit-</td>
<td class="bits">XXXX XXXX XXXX XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX</td>
<td class="bits"><span class="free">OOO</span>X XXXX</td>
<td class="bits"><span class="free">OOOO</span> XXXX</td>
<td class="bits">-inherit-</td>
<td class="bits">XX<span class="free">OO O</span>XXX</td>
@@ -154,7 +145,7 @@ the array so you can quickly see what is used and what is not.
<td class="caption">station</td>
<td class="bits">XXXX XXXX</td>
<td class="bits">XXXX XXXX XXXX XXXX</td>
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OOO</span>X XXXX</td>
<td class="bits">XXXX XXXX</td>
<td class="bits">XXXX XXXX</td>
<td class="bits">XXXX XXXX</td>
@@ -225,26 +216,17 @@ the array so you can quickly see what is used and what is not.
<td class="bits">-inherit-</td>
</tr>
<tr>
<td rowspan=3>9</td>
<td rowspan=2>9</td>
<td class="caption">tunnel</td>
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO</span> XXXX</td>
<td class="bits">X<span class="free">OOO OOOO</span></td>
<td class="bits">XXXX XXXX</td>
<td class="bits">X<span class="free">OOO</span> XXXX</td>
<td class="bits">XXXX XXXX</td>
</tr>
<tr>
<td>bridge ramp</td>
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OOOO OOOO</span> <span class="abuse">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="free">OOOO</span> XXXX</td>
<td class="bits">X<span class="free">OOO OOOO</span></td>
<td class="bits">XXXX XXXX</td>
<td class="bits">XXX<span class="free">O O</span>XXX</td>
</tr>
<tr>
<td>bridge middle part</td>
<td>bridge</td>
<td class="bits">XXXX XXXX</td>
<td class="bits"><span class="free">OOOO OOOO</span> <span class="abuse">XXXX XXXX</span></td>
<td class="bits">XXXX XXXX</td>

View File

@@ -9,7 +9,7 @@ Multiplayer Manual for OpenTTD (0.3.5)
- Type in a game name
- Select the type of game ('LAN/Internet' or 'Internet (advertise)'. With the last one
other people are able to see you online. Else they need your IP and port to join)
- Click "start game", "load game" or "load scenario"
- Click "start game" ,"load game" or "load scenario"
- Start playing
@@ -46,7 +46,7 @@ Multiplayer Manual for OpenTTD (0.3.5)
- Open the console and type in the following command:
connect <ip/host>:<port>#<player-no>
]connect <ip/host>:<port>#<player-no>
4. Playing Internet-Games
@@ -54,9 +54,7 @@ Multiplayer Manual for OpenTTD (0.3.5)
- Since OpenTTD 0.3.5 the network protocol has been rewritten and is very stable, even over slow connections.
- Servers with a red dot behind it have a different version then you have. You will not be able to join those servers.
- Servers with a yellow dot behind it have NewGRFs that you do not have. You will not be able to join those servers.
- Server with a red dot behind it have a different version then you have. You will not be able to join those servers.
- It can happen that a connection is that slow, or you have that many clients connected to your server, that your clients start to loose their connection. Some things you can do about it:
@@ -112,10 +110,3 @@ Multiplayer Manual for OpenTTD (0.3.5)
- From 0.3.5, desyncs should not happen anymore
- From 0.3.5, patch-settings are also synced. You can now play without deleting openttd.cfg, and with, for example, extra large trains enabled.
7. Troubleshooting
------------------
- My advertised server does not show up in the 'advertised server list' (servers.openttd.org)
Run openttd with the '-d net=2' parameter, as this will show whether it receives replies from the master server.
If it does not receive replies it is most likely that you need to configure your router to forward the OpenTTD ports 3979 (both TCP and UDP) to the computer that is hosting the game.

View File

@@ -1,6 +1,6 @@
.\" Hey, EMACS: -*- nroff -*-
.\" Please adjust this date whenever revising the manpage.
.Dd May 29, 2007
.Dd August 12, 2006
.Dt OPENTTD 6
.Sh NAME
.Nm openttd
@@ -11,14 +11,14 @@
.Op Fl G Ar seed
.Op Fl d Ar [level | cat=lvl[, ...]]
.Op Fl g Ar [savegame]
.Op Fl n Ar host[:port][#player]
.Op Fl n Ar [host[#player][:port]]
.Op Fl r Ar widthxheight
.Op Fl t Ar date
.Op Fl m Ar driver
.Op Fl s Ar driver
.Op Fl v Ar driver
.Sh OPTIONS
.Bl -tag -width ".Fl n Ar host[:port][#player]"
.Bl -tag -width ".Fl n Ar host[#player][:port]"
.It Fl D
Start a dedicated server
.It Fl G Ar seed
@@ -45,7 +45,9 @@ Force to use the DOS palette (use this if you see a lot of magenta)
.It Fl m Ar driver
Set the music driver, see
.Fl h
.It Fl n Ar host[:port][#player]
.It Fl n
Start a network server
.It Fl n Ar host[#player][:port]
Join a network game, optionally specify player to play as and port to connect to
.It Fl r Ar widthxheight
Set the resolution
@@ -59,7 +61,7 @@ Set the video driver, see
.Fl h
.El
.Sh SEE ALSO
http://wiki.openttd.org/, http://www.openttd.org
http://wiki.openttd.org/
.Sh HISTORY
Transport Tycoon Deluxe was written by Chris Sawyer and published by Microprose.
.Nm

View File

@@ -45,7 +45,7 @@ static const DriverDesc _music_driver_descs[] = {
#ifdef __BEOS__
M("bemidi", "BeOS MIDI Driver", &_bemidi_music_driver),
#endif
#if defined(__OS2__) && !defined(__INNOTEK_LIBC__)
#ifdef __OS2__
M("os2", "OS/2 Music Driver", &_os2_music_driver),
#endif
#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT
@@ -206,18 +206,18 @@ int GetDriverParamInt(const char* const* parm, const char* name, int def)
}
char *GetDriverList(char* p, const char *last)
char *GetDriverList(char* p)
{
const DriverClass* dc;
for (dc = _driver_classes; dc != endof(_driver_classes); dc++) {
const DriverDesc* dd;
p += snprintf(p, last - p, "List of %s drivers:\n", dc->name);
p += sprintf(p, "List of %s drivers:\n", dc->name);
for (dd = dc->descs; dd->name != NULL; dd++) {
p += snprintf(p, last - p, "%10s: %s\n", dd->name, dd->longname);
p += sprintf(p, "%10s: %s\n", dd->name, dd->longname);
}
p = strecpy(p, "\n", last);
p += sprintf(p, "\n");
}
return p;

View File

@@ -8,6 +8,6 @@ void LoadDriver(int driver, const char *name);
bool GetDriverParamBool(const char* const* parm, const char* name);
int GetDriverParamInt(const char* const* parm, const char* name, int def);
char *GetDriverList(char *p, const char *last);
char *GetDriverList(char* p);
#endif /* DRIVER_H */

View File

@@ -14,14 +14,14 @@ static void DrawTile_Dummy(TileInfo *ti)
}
static uint GetSlopeZ_Dummy(TileIndex tile, uint x, uint y)
static uint GetSlopeZ_Dummy(const TileInfo* ti)
{
return 0;
}
static Slope GetSlopeTileh_Dummy(TileIndex tile, Slope tileh)
static uint GetSlopeTileh_Dummy(const TileInfo* ti)
{
return SLOPE_FLAT;
return 0;
}
static int32 ClearTile_Dummy(TileIndex tile, byte flags)
@@ -67,17 +67,18 @@ static uint32 GetTileTrackStatus_Dummy(TileIndex tile, TransportType mode)
}
const TileTypeProcs _tile_type_dummy_procs = {
DrawTile_Dummy, /* draw_tile_proc */
GetSlopeZ_Dummy, /* get_slope_z_proc */
ClearTile_Dummy, /* clear_tile_proc */
GetAcceptedCargo_Dummy, /* get_accepted_cargo_proc */
GetTileDesc_Dummy, /* get_tile_desc_proc */
GetTileTrackStatus_Dummy, /* get_tile_track_status_proc */
ClickTile_Dummy, /* click_tile_proc */
AnimateTile_Dummy, /* animate_tile_proc */
TileLoop_Dummy, /* tile_loop_clear */
ChangeTileOwner_Dummy, /* change_tile_owner_clear */
NULL, /* get_produced_cargo_proc */
NULL, /* vehicle_enter_tile_proc */
GetSlopeTileh_Dummy, /* get_slope_tileh_proc */
DrawTile_Dummy, /* draw_tile_proc */
GetSlopeZ_Dummy, /* get_slope_z_proc */
ClearTile_Dummy, /* clear_tile_proc */
GetAcceptedCargo_Dummy, /* get_accepted_cargo_proc */
GetTileDesc_Dummy, /* get_tile_desc_proc */
GetTileTrackStatus_Dummy, /* get_tile_track_status_proc */
ClickTile_Dummy, /* click_tile_proc */
AnimateTile_Dummy, /* animate_tile_proc */
TileLoop_Dummy, /* tile_loop_clear */
ChangeTileOwner_Dummy, /* change_tile_owner_clear */
NULL, /* get_produced_cargo_proc */
NULL, /* vehicle_enter_tile_proc */
NULL, /* vehicle_leave_tile_proc */
GetSlopeTileh_Dummy, /* get_slope_tileh_proc */
};

833
economy.c

File diff suppressed because it is too large Load Diff

View File

@@ -21,50 +21,50 @@ typedef struct {
VARDEF Economy _economy;
typedef struct Subsidy {
CargoID cargo_type;
byte cargo_type;
byte age;
/* from and to can either be TownID, StationID or IndustryID */
uint16 from;
uint16 to;
} Subsidy;
enum {
SCORE_VEHICLES = 0,
SCORE_STATIONS = 1,
SCORE_MIN_PROFIT = 2,
SCORE_MIN_INCOME = 3,
SCORE_MAX_INCOME = 4,
SCORE_DELIVERED = 5,
SCORE_CARGO = 6,
SCORE_MONEY = 7,
SCORE_LOAN = 8,
SCORE_TOTAL = 9, // This must always be the last entry
SCORE_VEHICLES = 0,
SCORE_STATIONS = 1,
SCORE_MIN_PROFIT = 2,
SCORE_MIN_INCOME = 3,
SCORE_MAX_INCOME = 4,
SCORE_DELIVERED = 5,
SCORE_CARGO = 6,
SCORE_MONEY = 7,
SCORE_LOAN = 8,
SCORE_TOTAL = 9, // This must always be the last entry
NUM_SCORE = 10, // How many scores are there..
NUM_SCORE = 10, // How many scores are there..
SCORE_MAX = 1000 // The max score that can be in the performance history
// the scores together of score_info is allowed to be more!
SCORE_MAX = 1000, // The max score that can be in the performance history
// the scores together of score_info is allowed to be more!
};
typedef struct ScoreInfo {
byte id; // Unique ID of the score
int needed; // How much you need to get the perfect score
int score; // How much score it will give
byte id; // Unique ID of the score
int needed; // How much you need to get the perfect score
int score; // How much score it will give
} ScoreInfo;
extern const ScoreInfo _score_info[];
extern int _score_part[MAX_PLAYERS][NUM_SCORE];
int UpdateCompanyRatingAndValue(Player *p, bool update);
void UpdatePlayerHouse(Player *p, uint score);
VARDEF Subsidy _subsidies[MAX_PLAYERS];
Pair SetupSubsidyDecodeParam(const Subsidy* s, bool mode);
void DeleteSubsidyWithTown(TownID index);
void DeleteSubsidyWithIndustry(IndustryID index);
void DeleteSubsidyWithStation(StationID index);
void DeleteSubsidyWithIndustry(uint16 index);
void DeleteSubsidyWithStation(uint16 index);
int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, byte cargo_type);
uint MoveGoodsToStation(TileIndex tile, int w, int h, int type, uint amount);
#endif /* ECONOMY_H */

455
elrail.c
View File

@@ -1,455 +0,0 @@
/* $Id$ */
/** @file elrail.c
* This file deals with displaying wires and pylons for electric railways.
* <h2>Basics</h2>
*
* <h3>Tile Types</h3>
*
* We have two different types of tiles in the drawing code:
* Normal Railway Tiles (NRTs) which can have more than one track on it, and
* Special Railways tiles (SRTs) which have only one track (like crossings, depots
* stations, etc).
*
* <h3>Location Categories</h3>
*
* All tiles are categorized into three location groups (TLG):
* Group 0: Tiles with both an even X coordinate and an even Y coordinate
* Group 1: Tiles with an even X and an odd Y coordinate
* Group 2: Tiles with an odd X and an even Y coordinate
* Group 3: Tiles with both an odd X and Y coordnate.
*
* <h3>Pylon Points</h3>
* <h4>Control Points</h4>
* A Pylon Control Point (PCP) is a position where a wire (or rather two)
* is mounted onto a pylon.
* Each NRT does contain 4 PCPs which are bitmapped to a byte
* variable and are represented by the DiagDirection enum
*
* Each track ends on two PCPs and thus requires one pylon on each end. However,
* there is one exception: Straight-and-level tracks only have one pylon every
* other tile.
*
* Now on each edge there are two PCPs: One from each adjacent tile. Both PCPs
* are merged using an OR operation (i. e. if one tile needs a PCP at the postion
* in question, both tiles get it).
*
* <h4>Position Points</h4>
* A Pylon Position Point (PPP) is a position where a pylon is located on the
* ground. Each PCP owns 8 in (45 degree steps) PPPs that are located around
* it. PPPs are represented using the Direction enum. Each track bit has PPPs
* that are impossible (because the pylon would be situated on the track) and
* some that are preferred (because the pylon would be rectangular to the track).
*
* <img src="../../elrail_tile.png">
* <img src="../../elrail_track.png">
*
*/
#include "stdafx.h"
#include "openttd.h"
#include "station_map.h"
#include "tile.h"
#include "viewport.h"
#include "functions.h" /* We should REALLY get rid of this goddamn file, as it is butt-ugly */
#include "variables.h" /* ... same here */
#include "rail.h"
#include "debug.h"
#include "tunnel_map.h"
#include "road_map.h"
#include "bridge_map.h"
#include "bridge.h"
#include "rail_map.h"
#include "table/sprites.h"
#include "table/elrail_data.h"
#include "vehicle.h"
#include "train.h"
#include "gui.h"
static inline TLG GetTLG(TileIndex t)
{
return (HASBIT(TileX(t), 0) << 1) + HASBIT(TileY(t), 0);
}
/** Finds which Rail Bits are present on a given tile. For bridge tiles,
* returns track bits under the bridge
*/
static TrackBits GetRailTrackBitsUniversal(TileIndex t, byte *override)
{
switch (GetTileType(t)) {
case MP_RAILWAY:
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
switch (GetRailTileType(t)) {
case RAIL_TILE_NORMAL: case RAIL_TILE_SIGNALS:
return GetTrackBits(t);
case RAIL_TILE_DEPOT_WAYPOINT:
if (GetRailTileSubtype(t) == RAIL_SUBTYPE_WAYPOINT) return GetRailWaypointBits(t);
default:
return 0;
}
break;
case MP_TUNNELBRIDGE:
if (IsTunnel(t)) {
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
if (override != NULL) *override = 1 << GetTunnelDirection(t);
return AxisToTrackBits(DiagDirToAxis(GetTunnelDirection(t)));
} else {
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
if (IsBridgeMiddle(t)) {
if (IsTransportUnderBridge(t) &&
GetTransportTypeUnderBridge(t) == TRANSPORT_RAIL) {
return GetRailBitsUnderBridge(t);
} else {
return 0;
}
} else {
if (override != NULL && DistanceMax(t, GetOtherBridgeEnd(t)) > 1) *override = 1 << GetBridgeRampDirection(t);
return AxisToTrackBits(DiagDirToAxis(GetBridgeRampDirection(t)));
}
}
case MP_STREET:
if (GetRoadTileType(t) != ROAD_TILE_CROSSING) return 0;
if (GetRailTypeCrossing(t) != RAILTYPE_ELECTRIC) return 0;
return GetCrossingRailBits(t);
case MP_STATION:
if (!IsRailwayStation(t)) return 0;
if (GetRailType(t) != RAILTYPE_ELECTRIC) return 0;
if (!IsStationTileElectrifiable(t)) return 0;
return TrackToTrackBits(GetRailStationTrack(t));
default:
return 0;
}
}
/** Corrects the tileh for certain tile types. Returns an effective tileh for the track on the tile.
* @param tile The tile to analyse
* @param *tileh the tileh
*/
static void AdjustTileh(TileIndex tile, Slope *tileh)
{
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
if (IsTunnel(tile)) {
*tileh = SLOPE_STEEP; /* XXX - Hack to make tunnel entrances to always have a pylon */
} else {
if (IsBridgeRamp(tile)) {
if (*tileh != SLOPE_FLAT) {
*tileh = SLOPE_FLAT;
} else {
switch (GetBridgeRampDirection(tile)) {
case DIAGDIR_NE: *tileh = SLOPE_NE; break;
case DIAGDIR_SE: *tileh = SLOPE_SE; break;
case DIAGDIR_SW: *tileh = SLOPE_SW; break;
case DIAGDIR_NW: *tileh = SLOPE_NW; break;
default: break;
}
}
}
}
}
}
/** Draws wires and, if required, pylons on a given tile
* @param ti The Tileinfo to draw the tile for
*/
static void DrawCatenaryRailway(const TileInfo *ti)
{
/* Pylons are placed on a tile edge, so we need to take into account
* the track configuration of 2 adjacent tiles. trackconfig[0] stores the
* current tile (home tile) while [1] holds the neighbour */
TrackBits trackconfig[TS_END];
bool isflat[TS_END];
/* Note that ti->tileh has already been adjusted for Foundations */
Slope tileh[TS_END] = { ti->tileh, SLOPE_FLAT };
TLG tlg = GetTLG(ti->tile);
byte PCPstatus = 0;
byte OverridePCP = 0;
byte PPPpreferred[DIAGDIR_END];
byte PPPallowed[DIAGDIR_END];
DiagDirection i;
Track t;
/* Find which rail bits are present, and select the override points.
* We don't draw a pylon:
* 1) INSIDE a tunnel (we wouldn't see it anyway)
* 2) on the "far" end of a bridge head (the one that connects to bridge middle),
* because that one is drawn on the bridge. Exception is for length 0 bridges
* which have no middle tiles */
trackconfig[TS_HOME] = GetRailTrackBitsUniversal(ti->tile, &OverridePCP);
/* If a track bit is present that is not in the main direction, the track is level */
isflat[TS_HOME] = trackconfig[TS_HOME] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
AdjustTileh(ti->tile, &tileh[TS_HOME]);
for (i = DIAGDIR_NE; i < DIAGDIR_END; i++) {
TileIndex neighbour = ti->tile + TileOffsByDiagDir(i);
uint foundation = 0;
int k;
/* Here's one of the main headaches. GetTileSlope does not correct for possibly
* existing foundataions, so we do have to do that manually later on.*/
tileh[TS_NEIGHBOUR] = GetTileSlope(neighbour, NULL);
trackconfig[TS_NEIGHBOUR] = GetRailTrackBitsUniversal(neighbour, NULL);
if (IsTunnelTile(neighbour) && i != GetTunnelDirection(neighbour)) trackconfig[TS_NEIGHBOUR] = 0;
isflat[TS_NEIGHBOUR] = trackconfig[TS_NEIGHBOUR] & (TRACK_BIT_HORZ | TRACK_BIT_VERT);
PPPpreferred[i] = 0xFF; /* We start with preferring everything (end-of-line in any direction) */
PPPallowed[i] = AllowedPPPonPCP[i];
/* We cycle through all the existing tracks at a PCP and see what
* PPPs we want to have, or may not have at all */
for (k = 0; k < NUM_TRACKS_AT_PCP; k++) {
/* Next to us, we have a bridge head, don't worry about that one, if it shows away from us */
if (TrackSourceTile[i][k] == TS_NEIGHBOUR &&
IsBridgeTile(neighbour) && IsBridgeRamp(neighbour) &&
GetBridgeRampDirection(neighbour) == ReverseDiagDir(i)) {
continue;
}
/* We check whether the track in question (k) is present in the tile
* (TrackSourceTile) */
if (HASBIT(trackconfig[TrackSourceTile[i][k]], TracksAtPCP[i][k])) {
/* track found, if track is in the neighbour tile, adjust the number
* of the PCP for preferred/allowed determination*/
DiagDirection PCPpos = (TrackSourceTile[i][k] == TS_HOME) ? i : ReverseDiagDir(i);
SETBIT(PCPstatus, i); /* This PCP is in use */
PPPpreferred[i] &= PreferredPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
PPPallowed[i] &= ~DisallowedPPPofTrackAtPCP[TracksAtPCP[i][k]][PCPpos];
}
}
/* Deactivate all PPPs if PCP is not used */
PPPpreferred[i] *= HASBIT(PCPstatus, i);
PPPallowed[i] *= HASBIT(PCPstatus, i);
/* A station is always "flat", so adjust the tileh accordingly */
if (IsTileType(neighbour, MP_STATION)) tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
/* Read the foundataions if they are present, and adjust the tileh */
if (IsTileType(neighbour, MP_RAILWAY) && GetRailType(neighbour) == RAILTYPE_ELECTRIC) foundation = GetRailFoundation(tileh[TS_NEIGHBOUR], trackconfig[TS_NEIGHBOUR]);
if (IsBridgeTile(neighbour) && IsBridgeRamp(neighbour)) {
foundation = GetBridgeFoundation(tileh[TS_NEIGHBOUR], DiagDirToAxis(GetBridgeRampDirection(neighbour)));
}
if (foundation != 0) {
if (foundation < 15) {
tileh[TS_NEIGHBOUR] = SLOPE_FLAT;
} else {
tileh[TS_NEIGHBOUR] = _inclined_tileh[foundation - 15];
}
}
AdjustTileh(neighbour, &tileh[TS_NEIGHBOUR]);
/* If we have a straight (and level) track, we want a pylon only every 2 tiles
* Delete the PCP if this is the case. */
/* Level means that the slope is the same, or the track is flat */
if (tileh[TS_HOME] == tileh[TS_NEIGHBOUR] || (isflat[TS_HOME] && isflat[TS_NEIGHBOUR])) {
for (k = 0; k < NUM_IGNORE_GROUPS; k++)
if (PPPpreferred[i] == IgnoredPCP[k][tlg][i]) CLRBIT(PCPstatus, i);
}
/* Now decide where we draw our pylons. First try the preferred PPPs, but they may not exist.
* In that case, we try the any of the allowed ones. if they don't exist either, don't draw
* anything. Note that the preferred PPPs still contain the end-of-line markers.
* Remove those (simply by ANDing with allowed, since these markers are never allowed) */
if ((PPPallowed[i] & PPPpreferred[i]) != 0) PPPallowed[i] &= PPPpreferred[i];
if (PPPallowed[i] != 0 && HASBIT(PCPstatus, i) && !HASBIT(OverridePCP, i)) {
for (k = 0; k < DIR_END; k++) {
byte temp = PPPorder[i][GetTLG(ti->tile)][k];
if (HASBIT(PPPallowed[i], temp)) {
uint x = ti->x + x_pcp_offsets[i] + x_ppp_offsets[temp];
uint y = ti->y + y_pcp_offsets[i] + y_ppp_offsets[temp];
/* Don't build the pylon if it would be outside the tile */
if (!HASBIT(OwnedPPPonPCP[i], temp)) {
/* We have a neighour that will draw it, bail out */
if (trackconfig[TS_NEIGHBOUR] != 0) break;
continue; /* No neighbour, go looking for a better position */
}
AddSortableSpriteToDraw(pylons_normal[temp], x, y, 1, 1, 10,
GetSlopeZ(ti->x + x_pcp_offsets[i], ti->y + y_pcp_offsets[i]));
break; /* We already have drawn a pylon, bail out */
}
}
}
}
/* Don't draw a wire under a low bridge */
if (IsBridgeTile(ti->tile) &&
IsBridgeMiddle(ti->tile) &&
!(_display_opt & DO_TRANS_BUILDINGS) &&
GetBridgeHeight(ti->tile) <= TilePixelHeight(ti->tile) + TILE_HEIGHT) {
return;
}
/* Drawing of pylons is finished, now draw the wires */
for (t = 0; t < TRACK_END; t++) {
if (HASBIT(trackconfig[TS_HOME], t)) {
byte PCPconfig;
const SortableSpriteStruct *sss;
int tileh_selector;
if (IsTunnelTile(ti->tile)) {
const SortableSpriteStruct* sss = &CatenarySpriteData_Tunnel[GetTunnelDirection(ti->tile)];
AddSortableSpriteToDraw(
sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
sss->x_size, sss->y_size, sss->z_size,
GetTileZ(ti->tile) + sss->z_offset
);
break;
}
PCPconfig = HASBIT(PCPstatus, PCPpositions[t][0]) +
(HASBIT(PCPstatus, PCPpositions[t][1]) << 1);
tileh_selector = !(tileh[TS_HOME] % 3) * tileh[TS_HOME] / 3; /* tileh for the slopes, 0 otherwise */
assert(PCPconfig != 0); /* We have a pylon on neither end of the wire, that doesn't work (since we have no sprites for that) */
assert(!IsSteepSlope(tileh[TS_HOME]));
sss = &CatenarySpriteData[Wires[tileh_selector][t][PCPconfig]];
AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
sss->x_size, sss->y_size, sss->z_size, GetSlopeZ(ti->x + min(sss->x_offset, TILE_SIZE - 1), ti->y + min(sss->y_offset, TILE_SIZE - 1)) + sss->z_offset);
}
}
}
static void DrawCatenaryOnBridge(const TileInfo *ti)
{
TileIndex end = GetSouthernBridgeEnd(ti->tile);
TileIndex start = GetOtherBridgeEnd(end);
uint length = GetBridgeLength(start, end);
uint num = DistanceMax(ti->tile, start);
uint height;
const SortableSpriteStruct *sss;
Axis axis = GetBridgeAxis(ti->tile);
TLG tlg = GetTLG(ti->tile);
CatenarySprite offset = axis == AXIS_X ? 0 : WIRE_Y_FLAT_BOTH - WIRE_X_FLAT_BOTH;
if ((length % 2) && num == length) {
/* Draw the "short" wire on the southern end of the bridge
* only needed if the length of the bridge is odd */
sss = &CatenarySpriteData[WIRE_X_FLAT_BOTH + offset];
} else {
/* Draw "long" wires on all other tiles of the bridge (one pylon every two tiles) */
sss = &CatenarySpriteData[WIRE_X_FLAT_SW + (num % 2) + offset];
}
height = GetBridgeHeight(ti->tile);
AddSortableSpriteToDraw( sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
sss->x_size, sss->y_size, sss->z_size, height + sss->z_offset
);
/* Finished with wires, draw pylons */
/* every other tile needs a pylon on the northern end */
if (num % 2) {
if (axis == AXIS_X) {
AddSortableSpriteToDraw(pylons_bridge[0 + HASBIT(tlg, 0)], ti->x, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, height);
} else {
AddSortableSpriteToDraw(pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y, 1, 1, 10, height);
}
}
/* need a pylon on the southern end of the bridge */
if (DistanceMax(ti->tile, start) == length) {
if (axis == AXIS_X) {
AddSortableSpriteToDraw(pylons_bridge[0 + HASBIT(tlg, 0)], ti->x + 16, ti->y + 4 + 8 * HASBIT(tlg, 0), 1, 1, 10, height);
} else {
AddSortableSpriteToDraw(pylons_bridge[2 + HASBIT(tlg, 1)], ti->x + 4 + 8 * HASBIT(tlg, 1), ti->y + 16, 1, 1, 10, height);
}
}
}
void DrawCatenary(const TileInfo *ti)
{
if (_patches.disable_elrails) return;
switch (GetTileType(ti->tile)) {
case MP_RAILWAY:
if ( IsRailDepot(ti->tile)) {
const SortableSpriteStruct* sss = &CatenarySpriteData_Depot[GetRailDepotDirection(ti->tile)];
AddSortableSpriteToDraw(
sss->image, ti->x + sss->x_offset, ti->y + sss->y_offset,
sss->x_size, sss->y_size, sss->z_size,
GetTileMaxZ(ti->tile) + sss->z_offset
);
return;
}
break;
case MP_TUNNELBRIDGE:
if (IsBridge(ti->tile) && IsBridgeMiddle(ti->tile) && GetRailTypeOnBridge(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenaryOnBridge(ti);
break;
case MP_STREET: break;
case MP_STATION: break;
default: return;
}
DrawCatenaryRailway(ti);
}
int32 SettingsDisableElrail(int32 p1)
{
EngineID e_id;
Vehicle* v;
Player *p;
bool disable = (p1 != 0);
/* we will now walk through all electric train engines and change their railtypes if it is the wrong one*/
const RailType old_railtype = disable ? RAILTYPE_ELECTRIC : RAILTYPE_RAIL;
const RailType new_railtype = disable ? RAILTYPE_RAIL : RAILTYPE_ELECTRIC;
/* walk through all train engines */
for (e_id = 0; e_id < NUM_TRAIN_ENGINES; e_id++) {
const RailVehicleInfo *rv_info = RailVehInfo(e_id);
Engine *e = GetEngine(e_id);
/* if it is an electric rail engine and its railtype is the wrong one */
if (rv_info->engclass == 2 && e->railtype == old_railtype) {
/* change it to the proper one */
e->railtype = new_railtype;
}
}
/* when disabling elrails, make sure that all existing trains can run on
* normal rail too */
if (disable) {
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_Train && v->u.rail.railtype == RAILTYPE_ELECTRIC) {
/* this railroad vehicle is now compatible only with elrail,
* so add there also normal rail compatibility */
v->u.rail.compatible_railtypes |= (1 << RAILTYPE_RAIL);
v->u.rail.railtype = RAILTYPE_RAIL;
SETBIT(v->u.rail.flags, VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL);
}
}
}
/* setup total power for trains */
FOR_ALL_VEHICLES(v) {
/* power is cached only for front engines */
if (v->type == VEH_Train && IsFrontEngine(v)) TrainPowerChanged(v);
}
FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index);
/* This resets the _last_built_railtype, which will be invalid for electric
* rails. It may have unintended consequences if that function is ever
* extended, though. */
ReinitGuiAfterToggleElrail(disable);
return 0;
}

901
engine.c

File diff suppressed because it is too large Load Diff

215
engine.h
View File

@@ -3,9 +3,11 @@
#ifndef ENGINE_H
#define ENGINE_H
/** @file engine.h */
/** @file engine.h
*/
#include "oldpool.h"
#include "sprite.h"
#include "pool.h"
typedef struct RailVehicleInfo {
byte image_index;
@@ -18,35 +20,28 @@ typedef struct RailVehicleInfo {
byte running_cost_class;
byte engclass; // 0: steam, 1: diesel, 2: electric
byte capacity;
CargoID cargo_type;
byte ai_rank;
byte cargo_type;
byte callbackmask; // see CallbackMask enum
uint16 pow_wag_power;
byte pow_wag_weight;
byte visual_effect; // NOTE: this is not 100% implemented yet, at the moment it is only used as a 'fallback' value
// for when the 'powered wagon' callback fails. But it should really also determine what
// kind of visual effect to generate for a vehicle (default, steam, diesel, electric).
// Same goes for the callback result, which atm is only used to check if a wagon is powered.
byte shorten_factor; // length on main map for this type is 8 - shorten_factor
byte user_def_data; ///! Property 0x25: "User-defined bit mask" Used only for (very few) NewGRF vehicles
byte shorten_factor; // length on main map for this type is 8 - shorten_factor
} RailVehicleInfo;
typedef struct ShipVehicleInfo {
byte image_index;
byte base_cost;
uint16 max_speed;
CargoID cargo_type;
byte cargo_type;
uint16 capacity;
byte running_cost;
byte sfx;
byte refittable;
} ShipVehicleInfo;
// Aircraft subtypes
enum {
AIR_CTOL = 1, // Conventional Take Off and Landing, i.e. planes
AIR_FAST = 2
};
typedef struct AircraftVehicleInfo {
byte image_index;
byte base_cost;
@@ -66,29 +61,25 @@ typedef struct RoadVehicleInfo {
byte sfx;
byte max_speed;
byte capacity;
CargoID cargo_type;
byte cargo_type;
} RoadVehicleInfo;
/** Information about a vehicle
* @see table/engines.h
*/
* @see table/engines.h
*/
typedef struct EngineInfo {
Date base_intro;
uint16 base_intro;
byte unk2; ///< Carriages have the highest bit set in this one
Year lifelength;
Year base_life;
byte load_amount;
byte lifelength;
byte base_life;
byte railtype:4;
byte climates:4;
uint32 refit_mask;
byte refit_cost;
byte misc_flags;
byte callbackmask;
} EngineInfo;
typedef struct Engine {
Date intro_date;
Date age;
uint16 intro_date;
uint16 age;
uint16 reliability;
uint16 reliability_spd_dec;
uint16 reliability_start, reliability_max, reliability_final;
@@ -99,18 +90,9 @@ typedef struct Engine {
byte preview_wait;
byte railtype;
byte player_avail;
byte type; // type, ie VEH_Road, VEH_Train, etc. Same as in vehicle.h
byte type; // type, ie VEH_Road, VEH_Train, etc. Same as in vehicle.h
} Engine;
/**
* EngineInfo.misc_flags is a bitmask, with the following values
*/
enum {
EF_RAIL_TILTS = 0, ///< Rail vehicle tilts in curves (unsupported)
EF_ROAD_TRAM = 0, ///< Road vehicle is a tram/light rail vehicle (unsup)
EF_USES_2CC = 1, ///< Vehicle uses two company colours
EF_RAIL_IS_MU = 2, ///< Rail vehicle is a multiple-unit (DMU/EMU)
};
enum {
RVI_MULTIHEAD = 1,
@@ -128,6 +110,102 @@ enum {
void AddTypeToEngines(void);
void StartupEngines(void);
enum GlobalCargo {
GC_PASSENGERS = 0,
GC_COAL = 1,
GC_MAIL = 2,
GC_OIL = 3,
GC_LIVESTOCK = 4,
GC_GOODS = 5,
GC_GRAIN = 6, // GC_WHEAT / GC_MAIZE
GC_WOOD = 7,
GC_IRON_ORE = 8,
GC_STEEL = 9,
GC_VALUABLES = 10, // GC_GOLD / GC_DIAMONDS
GC_PAPER = 11,
GC_FOOD = 12,
GC_FRUIT = 13,
GC_COPPER_ORE = 14,
GC_WATER = 15,
GC_RUBBER = 16,
GC_SUGAR = 17,
GC_TOYS = 18,
GC_BATTERIES = 19,
GC_CANDY = 20,
GC_TOFFEE = 21,
GC_COLA = 22,
GC_COTTON_CANDY = 23,
GC_BUBBLES = 24,
GC_PLASTIC = 25,
GC_FIZZY_DRINKS = 26,
GC_PAPER_TEMP = 27,
GC_UNDEFINED = 28, // undefined; unused slot in arctic climate
GC_DEFAULT = 29,
GC_PURCHASE = 30,
GC_INVALID = 255,
NUM_GLOBAL_CID = 31
};
// This enum lists the implemented callbacks
// Use as argument for the GetCallBackResult function (see comments there)
enum CallbackID {
// Powered wagons, if the result is lower as 0x40 then the wagon is powered
// TODO: interpret the rest of the result, aka "visual effects"
CBID_WAGON_POWER = 0x10,
// Vehicle length, returns the amount of 1/8's the vehicle is shorter
// only for train vehicles
CBID_VEH_LENGTH = 0x11,
// Refit capacity, the passed vehicle needs to have its ->cargo_type set to
// the cargo we are refitting to, returns the new cargo capacity
CBID_REFIT_CAP = 0x15,
CBID_ARTIC_ENGINE = 0x16,
};
// bit positions for rvi->callbackmask, indicates which callbacks are used by an engine
// (some callbacks are always used, and dont appear here)
enum CallbackMask {
CBM_WAGON_POWER = 0,
CBM_VEH_LENGTH = 1,
CBM_REFIT_CAP = 3,
CBM_ARTIC_ENGINE = 4,
};
enum {
CALLBACK_FAILED = 0xFFFF
};
VARDEF const uint32 _default_refitmasks[NUM_VEHICLE_TYPES];
VARDEF const CargoID _global_cargo_id[NUM_LANDSCAPE][NUM_CARGO];
VARDEF const uint32 _landscape_global_cargo_mask[NUM_LANDSCAPE];
VARDEF const CargoID _local_cargo_id_ctype[NUM_GLOBAL_CID];
VARDEF const uint32 cargo_classes[16];
void SetWagonOverrideSprites(EngineID engine, struct SpriteGroup *group, byte *train_id, int trains);
void SetCustomEngineSprites(EngineID engine, byte cargo, struct SpriteGroup *group);
// loaded is in percents, overriding_engine 0xffff is none
int GetCustomEngineSprite(EngineID engine, const Vehicle *v, byte direction);
uint16 GetCallBackResult(uint16 callback_info, EngineID engine, const Vehicle *v);
bool UsesWagonOverride(const Vehicle *v);
#define GetCustomVehicleSprite(v, direction) GetCustomEngineSprite(v->engine_type, v, direction)
#define GetCustomVehicleIcon(et, direction) GetCustomEngineSprite(et, NULL, direction)
typedef enum VehicleTrigger {
VEHICLE_TRIGGER_NEW_CARGO = 1,
// Externally triggered only for the first vehicle in chain
VEHICLE_TRIGGER_DEPOT = 2,
// Externally triggered only for the first vehicle in chain, only if whole chain is empty
VEHICLE_TRIGGER_EMPTY = 4,
// Not triggered externally (called for the whole chain if we got NEW_CARGO)
VEHICLE_TRIGGER_ANY_NEW_CARGO = 8,
} VehicleTrigger;
void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger);
void SetCustomEngineName(EngineID engine, const char *name);
StringID GetCustomEngineName(EngineID engine);
void DrawTrainEngine(int x, int y, EngineID engine, uint32 image_ormod);
void DrawRoadVehEngine(int x, int y, EngineID engine, uint32 image_ormod);
@@ -137,28 +215,28 @@ void DrawAircraftEngine(int x, int y, EngineID engine, uint32 image_ormod);
void LoadCustomEngineNames(void);
void DeleteCustomEngineNames(void);
bool IsEngineBuildable(EngineID engine, byte type, PlayerID player);
bool IsEngineBuildable(uint engine, byte type);
enum {
NUM_NORMAL_RAIL_ENGINES = 54,
NUM_MONORAIL_ENGINES = 30,
NUM_MAGLEV_ENGINES = 32,
NUM_TRAIN_ENGINES = NUM_NORMAL_RAIL_ENGINES + NUM_MONORAIL_ENGINES + NUM_MAGLEV_ENGINES,
NUM_ROAD_ENGINES = 88,
NUM_SHIP_ENGINES = 11,
NUM_AIRCRAFT_ENGINES = 41,
TOTAL_NUM_ENGINES = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES + NUM_AIRCRAFT_ENGINES,
AIRCRAFT_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES,
SHIP_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES,
ROAD_ENGINES_INDEX = NUM_TRAIN_ENGINES,
NUM_MONORAIL_ENGINES = 30,
NUM_MAGLEV_ENGINES = 32,
NUM_TRAIN_ENGINES = NUM_NORMAL_RAIL_ENGINES + NUM_MONORAIL_ENGINES + NUM_MAGLEV_ENGINES,
NUM_ROAD_ENGINES = 88,
NUM_SHIP_ENGINES = 11,
NUM_AIRCRAFT_ENGINES = 41,
TOTAL_NUM_ENGINES = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES + NUM_AIRCRAFT_ENGINES,
AIRCRAFT_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES,
SHIP_ENGINES_INDEX = NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES,
ROAD_ENGINES_INDEX = NUM_TRAIN_ENGINES,
};
VARDEF Engine _engines[TOTAL_NUM_ENGINES];
#define FOR_ALL_ENGINES(e) for (e = _engines; e != endof(_engines); e++)
static inline Engine* GetEngine(EngineID i)
{
assert(i < lengthof(_engines));
return &_engines[i];
assert(i < lengthof(_engines));
return &_engines[i];
}
VARDEF StringID _engine_name_strings[TOTAL_NUM_ENGINES];
@@ -182,12 +260,6 @@ extern ShipVehicleInfo _ship_vehicle_info[NUM_SHIP_ENGINES];
extern AircraftVehicleInfo _aircraft_vehicle_info[NUM_AIRCRAFT_ENGINES];
extern RoadVehicleInfo _road_vehicle_info[NUM_ROAD_ENGINES];
static inline const EngineInfo *EngInfo(EngineID e)
{
assert(e < lengthof(_engine_info));
return &_engine_info[e];
}
static inline const RailVehicleInfo* RailVehInfo(EngineID e)
{
assert(e < lengthof(_rail_vehicle_info));
@@ -212,6 +284,10 @@ static inline const RoadVehicleInfo* RoadVehInfo(EngineID e)
return &_road_vehicle_info[e - ROAD_ENGINES_INDEX];
}
void UnloadWagonOverrides(void);
void UnloadCustomEngineSprites(void);
void UnloadCustomEngineNames(void);
/************************************************************************
* Engine Replacement stuff
************************************************************************/
@@ -222,7 +298,7 @@ static inline const RoadVehicleInfo* RoadVehInfo(EngineID e)
* it.
*/
struct EngineRenew {
EngineRenewID index;
uint16 index;
EngineID from;
EngineID to;
struct EngineRenew *next;
@@ -235,24 +311,18 @@ typedef struct EngineRenew EngineRenew;
* placed here so the only exception to this rule, the saveload code, can use
* it.
*/
DECLARE_OLD_POOL(EngineRenew, EngineRenew, 3, 8000)
extern MemoryPool _engine_renew_pool;
/**
* Check if a EngineRenew really exists.
* DO NOT USE outside of engine.c. Is
* placed here so the only exception to this rule, the saveload code, can use
* it.
*/
static inline bool IsValidEngineRenew(const EngineRenew *er)
static inline EngineRenew *GetEngineRenew(uint16 index)
{
return er->from != INVALID_ENGINE;
return (EngineRenew*)GetItemFromPool(&_engine_renew_pool, index);
}
static inline void DeleteEngineRenew(EngineRenew *er)
{
er->from = INVALID_ENGINE;
}
#define FOR_ALL_ENGINE_RENEWS_FROM(er, start) for (er = GetEngineRenew(start); er != NULL; er = (er->index + 1U < GetEngineRenewPoolSize()) ? GetEngineRenew(er->index + 1U) : NULL) if (er->from != INVALID_ENGINE) if (IsValidEngineRenew(er))
#define FOR_ALL_ENGINE_RENEWS(er) FOR_ALL_ENGINE_RENEWS_FROM(er, 0)
/**
* A list to group EngineRenew directives together (such as per-player).
@@ -294,15 +364,4 @@ int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID n
*/
int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, uint32 flags);
/* Engine list manipulators - current implementation is only C wrapper of CBlobT<EngineID> class (helpers.cpp) */
void EngList_Create(EngineList *el); ///< Creates engine list
void EngList_Destroy(EngineList *el); ///< Deallocate and destroy engine list
uint EngList_Count(const EngineList *el); ///< Returns number of items in the engine list
void EngList_Add(EngineList *el, EngineID eid); ///< Append one item at the end of engine list
EngineID* EngList_Items(EngineList *el); ///< Returns engine list items as C array
void EngList_RemoveAll(EngineList *el); ///< Removes all items from engine list
typedef int CDECL EngList_SortTypeFunction(const void*, const void*); ///< argument type for EngList_Sort()
void EngList_Sort(EngineList *el, EngList_SortTypeFunction compare); ///< qsort of the engine list
void EngList_SortPartial(EngineList *el, EngList_SortTypeFunction compare, uint begin, uint num_items); ///< qsort of specified portion of the engine list
#endif /* ENGINE_H */

View File

@@ -13,34 +13,33 @@
#include "command.h"
#include "news.h"
#include "variables.h"
#include "newgrf_engine.h"
#include "vehicle.h"
static StringID GetEngineCategoryName(EngineID engine)
{
switch (GetEngine(engine)->type) {
default: NOT_REACHED();
case VEH_Road: return STR_8103_ROAD_VEHICLE;
case VEH_Aircraft: return STR_8104_AIRCRAFT;
case VEH_Ship: return STR_8105_SHIP;
case VEH_Train:
switch (GetEngine(engine)->railtype) {
default: NOT_REACHED();
case RAILTYPE_RAIL: return STR_8102_RAILROAD_LOCOMOTIVE;
case RAILTYPE_ELECTRIC: return STR_8102_RAILROAD_LOCOMOTIVE;
case RAILTYPE_MONO: return STR_8106_MONORAIL_LOCOMOTIVE;
case RAILTYPE_MAGLEV: return STR_8107_MAGLEV_LOCOMOTIVE;
}
if (engine < NUM_TRAIN_ENGINES) {
switch (GetEngine(engine)->railtype) {
case RAILTYPE_RAIL: return STR_8102_RAILROAD_LOCOMOTIVE;
case RAILTYPE_MONO: return STR_8106_MONORAIL_LOCOMOTIVE;
case RAILTYPE_MAGLEV: return STR_8107_MAGLEV_LOCOMOTIVE;
}
}
if (engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES)
return STR_8103_ROAD_VEHICLE;
if (engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES)
return STR_8105_SHIP;
return STR_8104_AIRCRAFT;
}
static const Widget _engine_preview_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 5, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CLOSEBOX, RESIZE_NONE, 5, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 5, 11, 299, 0, 13, STR_8100_MESSAGE_FROM_VEHICLE_MANUFACTURE, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PANEL, RESIZE_NONE, 5, 0, 299, 14, 191, 0x0, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 5, 85, 144, 172, 183, STR_00C9_NO, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 5, 155, 214, 172, 183, STR_00C8_YES, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 5, 0, 299, 14, 191, 0x0, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 5, 85, 144, 172, 183, STR_00C9_NO, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 5, 155, 214, 172, 183, STR_00C8_YES, STR_NULL},
{ WIDGETS_END},
};
@@ -58,10 +57,10 @@ static void DrawShipEngineInfo(EngineID engine, int x, int y, int maxw);
static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw);
static const DrawEngineInfo _draw_engine_list[4] = {
{ DrawTrainEngine, DrawTrainEngineInfo },
{ DrawRoadVehEngine, DrawRoadVehEngineInfo },
{ DrawShipEngine, DrawShipEngineInfo },
{ DrawAircraftEngine, DrawAircraftEngineInfo },
{DrawTrainEngine,DrawTrainEngineInfo},
{DrawRoadVehEngine,DrawRoadVehEngineInfo},
{DrawShipEngine,DrawShipEngineInfo},
{DrawAircraftEngine,DrawAircraftEngineInfo},
};
static void EnginePreviewWndProc(Window *w, WindowEvent *e)
@@ -79,7 +78,10 @@ static void EnginePreviewWndProc(Window *w, WindowEvent *e)
DrawStringCentered(w->width >> 1, 80, GetCustomEngineName(engine), 0x10);
dei = &_draw_engine_list[GetEngine(engine)->type - VEH_Train];
(dei = _draw_engine_list,engine < NUM_TRAIN_ENGINES) ||
(dei++,engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES) ||
(dei++,engine < NUM_TRAIN_ENGINES + NUM_ROAD_ENGINES + NUM_SHIP_ENGINES) ||
(dei++, true);
width = w->width;
dei->engine_proc(width >> 1, 100, engine, 0);
@@ -88,11 +90,13 @@ static void EnginePreviewWndProc(Window *w, WindowEvent *e)
}
case WE_CLICK:
switch (e->we.click.widget) {
switch (e->click.widget) {
case 3:
DeleteWindow(w);
break;
case 4:
DoCommandP(0, w->window_number, 0, NULL, CMD_WANT_ENGINE_PREVIEW);
/* Fallthrough */
case 3:
DeleteWindow(w);
break;
}
@@ -111,7 +115,10 @@ static const WindowDesc _engine_preview_desc = {
void ShowEnginePreviewWindow(EngineID engine)
{
AllocateWindowDescFront(&_engine_preview_desc, engine);
Window *w;
w = AllocateWindowDesc(&_engine_preview_desc);
w->window_number = engine;
}
static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw)
@@ -120,26 +127,55 @@ static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw)
uint multihead = (rvi->flags & RVI_MULTIHEAD) ? 1 : 0;
SetDParam(0, (_price.build_railvehicle >> 3) * rvi->base_cost >> 5);
SetDParam(2, rvi->max_speed);
SetDParam(2, rvi->max_speed * 10 >> 4);
SetDParam(3, rvi->power << multihead);
SetDParam(1, rvi->weight << multihead);
SetDParam(4, rvi->running_cost_base * _price.running_rail[rvi->running_cost_class] >> 8 << multihead);
if (rvi->capacity != 0) {
SetDParam(5, rvi->cargo_type);
SetDParam(5, _cargoc.names_long[rvi->cargo_type]);
SetDParam(6, rvi->capacity << multihead);
} else {
SetDParam(5, CT_INVALID);
SetDParam(5, STR_8838_N_A);
}
DrawStringMultiCenter(x, y, STR_VEHICLE_INFO_COST_WEIGHT_SPEED_POWER, maxw);
DrawStringMultiCenter(x, y, STR_885B_COST_WEIGHT_T_SPEED_POWER, maxw);
}
void DrawNewsNewTrainAvail(Window *w)
{
EngineID engine;
DrawNewsBorder(w);
engine = WP(w,news_d).ni->string_id;
SetDParam(0, GetEngineCategoryName(engine));
DrawStringMultiCenter(w->width >> 1, 20, STR_8859_NEW_NOW_AVAILABLE, w->width - 2);
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
SetDParam(0, GetCustomEngineName(engine));
DrawStringMultiCenter(w->width >> 1, 57, STR_885A, w->width - 2);
DrawTrainEngine(w->width >> 1, 88, engine, 0);
GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
DrawTrainEngineInfo(engine, w->width >> 1, 129, w->width - 52);
}
StringID GetNewsStringNewTrainAvail(const NewsItem *ni)
{
EngineID engine = ni->string_id;
SetDParam(0, STR_8859_NEW_NOW_AVAILABLE);
SetDParam(1, GetEngineCategoryName(engine));
SetDParam(2, GetCustomEngineName(engine));
return STR_02B6;
}
static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw)
{
const AircraftVehicleInfo *avi = AircraftVehInfo(engine);
SetDParam(0, (_price.aircraft_base >> 3) * avi->base_cost >> 5);
SetDParam(1, avi->max_speed * 128 / 10);
SetDParam(1, avi->max_speed << 3);
SetDParam(2, avi->passenger_capacity);
SetDParam(3, avi->mail_capacity);
SetDParam(4, avi->running_cost * _price.aircraft_running >> 8);
@@ -147,54 +183,107 @@ static void DrawAircraftEngineInfo(EngineID engine, int x, int y, int maxw)
DrawStringMultiCenter(x, y, STR_A02E_COST_MAX_SPEED_CAPACITY, maxw);
}
void DrawNewsNewAircraftAvail(Window *w)
{
EngineID engine;
DrawNewsBorder(w);
engine = WP(w,news_d).ni->string_id;
DrawStringMultiCenter(w->width >> 1, 20, STR_A02C_NEW_AIRCRAFT_NOW_AVAILABLE, w->width - 2);
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
SetDParam(0, GetCustomEngineName(engine));
DrawStringMultiCenter(w->width >> 1, 57, STR_A02D, w->width - 2);
DrawAircraftEngine(w->width >> 1, 93, engine, 0);
GfxFillRect(25, 56, w->width - 56, 110, 0x323 | USE_COLORTABLE);
DrawAircraftEngineInfo(engine, w->width >> 1, 131, w->width - 52);
}
StringID GetNewsStringNewAircraftAvail(const NewsItem *ni)
{
EngineID engine = ni->string_id;
SetDParam(0, STR_A02C_NEW_AIRCRAFT_NOW_AVAILABLE);
SetDParam(1, GetCustomEngineName(engine));
return STR_02B6;
}
static void DrawRoadVehEngineInfo(EngineID engine, int x, int y, int maxw)
{
const RoadVehicleInfo *rvi = RoadVehInfo(engine);
SetDParam(0, (_price.roadveh_base >> 3) * rvi->base_cost >> 5);
SetDParam(1, rvi->max_speed / 2);
SetDParam(1, rvi->max_speed * 10 >> 5);
SetDParam(2, rvi->running_cost * _price.roadveh_running >> 8);
SetDParam(3, rvi->cargo_type);
SetDParam(4, rvi->capacity);
SetDParam(3, _cargoc.names_long[rvi->cargo_type]);
DrawStringMultiCenter(x, y, STR_902A_COST_SPEED_RUNNING_COST, maxw);
}
void DrawNewsNewRoadVehAvail(Window *w)
{
EngineID engine;
DrawNewsBorder(w);
engine = WP(w,news_d).ni->string_id;
DrawStringMultiCenter(w->width >> 1, 20, STR_9028_NEW_ROAD_VEHICLE_NOW_AVAILABLE, w->width - 2);
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
SetDParam(0, GetCustomEngineName(engine));
DrawStringMultiCenter(w->width >> 1, 57, STR_9029, w->width - 2);
DrawRoadVehEngine(w->width >> 1, 88, engine, 0);
GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
DrawRoadVehEngineInfo(engine, w->width >> 1, 129, w->width - 52);
}
StringID GetNewsStringNewRoadVehAvail(const NewsItem *ni)
{
EngineID engine = ni->string_id;
SetDParam(0, STR_9028_NEW_ROAD_VEHICLE_NOW_AVAILABLE);
SetDParam(1, GetCustomEngineName(engine));
return STR_02B6;
}
static void DrawShipEngineInfo(EngineID engine, int x, int y, int maxw)
{
const ShipVehicleInfo *svi = ShipVehInfo(engine);
SetDParam(0, svi->base_cost * (_price.ship_base >> 3) >> 5);
SetDParam(1, svi->max_speed / 2);
SetDParam(2, svi->cargo_type);
SetDParam(1, svi->max_speed * 10 >> 5);
SetDParam(2, _cargoc.names_long[svi->cargo_type]);
SetDParam(3, svi->capacity);
SetDParam(4, svi->running_cost * _price.ship_running >> 8);
DrawStringMultiCenter(x, y, STR_982E_COST_MAX_SPEED_CAPACITY, maxw);
}
StringID GetNewsStringNewVehicleAvail(const NewsItem *ni)
{
EngineID engine = ni->string_id;
SetDParam(0, GetEngineCategoryName(engine));
SetDParam(1, GetCustomEngineName(engine));
return STR_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE;
}
void DrawNewsNewVehicleAvail(Window *w)
void DrawNewsNewShipAvail(Window *w)
{
EngineID engine = WP(w, news_d).ni->string_id;
const DrawEngineInfo *dei = &_draw_engine_list[GetEngine(engine)->type - VEH_Train];
EngineID engine;
DrawNewsBorder(w);
SetDParam(0, GetEngineCategoryName(engine));
DrawStringMultiCenter(w->width >> 1, 20, STR_NEW_VEHICLE_NOW_AVAILABLE, w->width - 2);
engine = WP(w,news_d).ni->string_id;
DrawStringMultiCenter(w->width >> 1, 20, STR_982C_NEW_SHIP_NOW_AVAILABLE, w->width - 2);
GfxFillRect(25, 56, w->width - 25, w->height - 2, 10);
SetDParam(0, GetCustomEngineName(engine));
DrawStringMultiCenter(w->width >> 1, 57, STR_NEW_VEHICLE_TYPE, w->width - 2);
DrawStringMultiCenter(w->width >> 1, 57, STR_982D, w->width - 2);
dei->engine_proc(w->width >> 1, 88, engine, 0);
GfxFillRect(25, 56, w->width - 56, 112, 0x323 | USE_COLORTABLE);
dei->info_proc(engine, w->width >> 1, 129, w->width - 52);
DrawShipEngine(w->width >> 1, 93, engine, 0);
GfxFillRect(25, 56, w->width - 56, 110, 0x323 | USE_COLORTABLE);
DrawShipEngineInfo(engine, w->width >> 1, 131, w->width - 52);
}
StringID GetNewsStringNewShipAvail(const NewsItem *ni)
{
EngineID engine = ni->string_id;
SetDParam(0, STR_982C_NEW_SHIP_NOW_AVAILABLE);
SetDParam(1, GetCustomEngineName(engine));
return STR_02B6;
}

104
fileio.c
View File

@@ -4,9 +4,11 @@
#include "openttd.h"
#include "fileio.h"
#include "functions.h"
#include "string.h"
#include "macros.h"
#include "variables.h"
#if defined(UNIX) || defined(__OS2__)
#include <ctype.h> // required for tolower()
#endif
/*************************************************/
/* FILE IO ROUTINES ******************************/
@@ -15,11 +17,11 @@
#define FIO_BUFFER_SIZE 512
typedef struct {
byte *buffer, *buffer_end; ///< position pointer in local buffer and last valid byte of buffer
uint32 pos; ///< current (system) position in file
FILE *cur_fh; ///< current file handle
FILE *handles[64]; ///< array of file handles we can have open
byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file
byte *buffer, *buffer_end;
uint32 pos;
FILE *cur_fh;
FILE *handles[32];
byte buffer_start[512];
} Fio;
static Fio _fio;
@@ -34,8 +36,7 @@ void FioSeekTo(uint32 pos, int mode)
{
if (mode == SEEK_CUR) pos += FioGetPos();
_fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
_fio.pos = pos;
fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
fseek(_fio.cur_fh, (_fio.pos=pos), SEEK_SET);
}
// Seek to a file and a position
@@ -44,7 +45,7 @@ void FioSeekToFile(uint32 pos)
FILE *f = _fio.handles[pos >> 24];
assert(f != NULL);
_fio.cur_fh = f;
FioSeekTo(GB(pos, 0, 24), SEEK_SET);
FioSeekTo(pos & 0xFFFFFF, SEEK_SET);
}
byte FioReadByte(void)
@@ -58,7 +59,7 @@ byte FioReadByte(void)
void FioSkipBytes(int n)
{
for (;;) {
for(;;) {
int m = min(_fio.buffer_end - _fio.buffer, n);
_fio.buffer += m;
n -= m;
@@ -68,6 +69,7 @@ void FioSkipBytes(int n)
}
}
uint16 FioReadWord(void)
{
byte b = FioReadByte();
@@ -103,13 +105,40 @@ void FioCloseAll(void)
FioCloseFile(i);
}
bool FioCheckFileExists(const char *filename)
bool FiosCheckFileExists(const char *filename)
{
FILE *f = FioFOpenFile(filename);
if (f == NULL) return false;
FILE *f;
char buf[MAX_PATH];
fclose(f);
return true;
sprintf(buf, "%s%s", _path.data_dir, filename);
f = fopen(buf, "rb");
#if !defined(WIN32)
if (f == NULL) {
char *s;
// Make lower case and try again
for(s=buf + strlen(_path.data_dir) - 1; *s != 0; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
#if defined SECOND_DATA_DIR
// tries in the 2nd data directory
if (f == NULL) {
sprintf(buf, "%s%s", _path.second_data_dir, filename);
for(s=buf + strlen(_path.second_data_dir) - 1; *s != 0; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
}
#endif
}
#endif
if (f == NULL)
return false;
else {
fclose(f);
return true;
}
}
FILE *FioFOpenFile(const char *filename)
@@ -117,19 +146,23 @@ FILE *FioFOpenFile(const char *filename)
FILE *f;
char buf[MAX_PATH];
snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, filename);
sprintf(buf, "%s%s", _path.data_dir, filename);
f = fopen(buf, "rb");
#if !defined(WIN32)
if (f == NULL) {
strtolower(buf + strlen(_paths.data_dir) - 1);
char *s;
// Make lower case and try again
for(s=buf + strlen(_path.data_dir) - 1; *s != 0; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
#if defined SECOND_DATA_DIR
// tries in the 2nd data directory
if (f == NULL) {
snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, filename);
strtolower(buf + strlen(_paths.second_data_dir) - 1);
sprintf(buf, "%s%s", _path.second_data_dir, filename);
for(s=buf + strlen(_path.second_data_dir) - 1; *s != 0; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
}
#endif
@@ -141,9 +174,38 @@ FILE *FioFOpenFile(const char *filename)
void FioOpenFile(int slot, const char *filename)
{
FILE *f = FioFOpenFile(filename);
FILE *f;
char buf[MAX_PATH];
if (f == NULL) error("Cannot open file '%s%s'", _paths.data_dir, filename);
sprintf(buf, "%s%s", _path.data_dir, filename);
f = fopen(buf, "rb");
#if !defined(WIN32)
if (f == NULL) {
char *s;
// Make lower case and try again
for(s=buf + strlen(_path.data_dir) - 1; *s != 0; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
#if defined SECOND_DATA_DIR
// tries in the 2nd data directory
if (f == NULL) {
sprintf(buf, "%s%s", _path.second_data_dir, filename);
for(s=buf + strlen(_path.second_data_dir) - 1; *s != 0; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
}
if (f == NULL)
sprintf(buf, "%s%s", _path.data_dir, filename); //makes it print the primary datadir path instead of the secundary one
#endif
}
#endif
if (f == NULL)
error("Cannot open file '%s'", buf);
FioCloseFile(slot); // if file was opened before, close it
_fio.handles[slot] = f;

View File

@@ -14,6 +14,6 @@ FILE *FioFOpenFile(const char *filename);
void FioOpenFile(int slot, const char *filename);
void FioReadBlock(void *ptr, uint size);
void FioSkipBytes(int n);
bool FioCheckFileExists(const char *filename);
bool FiosCheckFileExists(const char *filename);
#endif /* FILEIO_H */

415
fios.c
View File

@@ -1,415 +0,0 @@
/* $Id$ */
/** @file fios.c
* This file contains functions for building file lists for the save/load dialogs.
*/
#include "stdafx.h"
#include "openttd.h"
#include "hal.h"
#include "string.h"
#include "variables.h"
#include "functions.h"
#include "heightmap.h"
#include "table/strings.h"
#include "fios.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
# include <tchar.h>
# include <io.h>
#else
# include <unistd.h>
#endif /* WIN32 */
/* Variables to display file lists */
int _fios_num;
static char *_fios_path;
static FiosItem *_fios_items;
static int _fios_count, _fios_alloc;
/* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
extern bool FiosIsRoot(const char *path);
extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
extern bool FiosIsHiddenFile(const struct dirent *ent);
extern void FiosGetDrives(void);
extern bool FiosGetDiskFreeSpace(const char *path, uint32 *tot);
/* get the name of an oldstyle savegame */
extern void GetOldSaveGameName(char *title, const char *path, const char *file);
/**
* Allocate a new FiosItem.
* @return A pointer to the newly allocated FiosItem.
*/
FiosItem *FiosAlloc(void)
{
if (_fios_count == _fios_alloc) {
_fios_alloc += 256;
_fios_items = realloc(_fios_items, _fios_alloc * sizeof(FiosItem));
}
return &_fios_items[_fios_count++];
}
/**
* Compare two FiosItem's. Used with qsort when sorting the file list.
* @param a A pointer to the first FiosItem to compare.
* @param a A pointer to the second FiosItem to compare.
* @return -1, 0 or 1, depending on how the two items should be sorted.
*/
int CDECL compare_FiosItems(const void *a, const void *b)
{
const FiosItem *da = (const FiosItem *)a;
const FiosItem *db = (const FiosItem *)b;
int r;
if (_savegame_sort_order & SORT_BY_NAME) {
r = strcasecmp(da->title, db->title);
} else {
r = da->mtime < db->mtime ? -1 : 1;
}
if (_savegame_sort_order & SORT_DESCENDING) r = -r;
return r;
}
/**
* Free the list of savegames
*/
void FiosFreeSavegameList(void)
{
free(_fios_items);
_fios_items = NULL;
_fios_alloc = _fios_count = 0;
}
/**
* Get descriptive texts. Returns the path and free space
* left on the device
* @param path string describing the path
* @param total_free total free space in megabytes, optional (can be NULL)
* @return StringID describing the path (free space or failure)
*/
StringID FiosGetDescText(const char **path, uint32 *total_free)
{
*path = _fios_path;
return FiosGetDiskFreeSpace(*path, total_free) ? STR_4005_BYTES_FREE : STR_4006_UNABLE_TO_READ_DRIVE;
}
/* Browse to a new path based on the passed FiosItem struct
* @param *item FiosItem object telling us what to do
* @return a string if we have given a file as a target, otherwise NULL */
char *FiosBrowseTo(const FiosItem *item)
{
char *s;
char *path = _fios_path;
switch (item->type) {
#if defined(WIN32) || defined(__OS2__)
case FIOS_TYPE_DRIVE: sprintf(path, "%c:" PATHSEP, item->title[0]); break;
#endif
case FIOS_TYPE_PARENT:
/* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
if ((s = strrchr(path, PATHSEPCHAR)) != NULL) {
s[1] = '\0'; // go up a directory
if (!FiosIsRoot(path)) s[0] = '\0';
}
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
else if ((s = strrchr(path, ':')) != NULL) s[1] = '\0';
#endif
break;
case FIOS_TYPE_DIR:
if (!FiosIsRoot(path)) strcat(path, PATHSEP);
strcat(path, item->name);
break;
case FIOS_TYPE_DIRECT:
sprintf(path, "%s" PATHSEP, item->name);
s = strrchr(path, PATHSEPCHAR);
if (s != NULL && s[1] == '\0') s[0] = '\0'; // strip trailing slash
break;
case FIOS_TYPE_FILE:
case FIOS_TYPE_OLDFILE:
case FIOS_TYPE_SCENARIO:
case FIOS_TYPE_OLD_SCENARIO:
case FIOS_TYPE_PNG:
case FIOS_TYPE_BMP:
{
static char str_buffr[512];
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
if (FiosIsRoot(path)) {
snprintf(str_buffr, lengthof(str_buffr), "%s:%s", path, item->name);
} else // XXX - only next line!
#endif
snprintf(str_buffr, lengthof(str_buffr), "%s" PATHSEP "%s", path, item->name);
return str_buffr;
}
}
return NULL;
}
void FiosMakeSavegameName(char *buf, const char *name, size_t size)
{
const char *extension, *period;
extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
/* Don't append the extension if it is already there */
period = strrchr(name, '.');
if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
}
#if defined(WIN32)
# define unlink _tunlink
#endif
bool FiosDelete(const char *name)
{
char filename[512];
FiosMakeSavegameName(filename, name, lengthof(filename));
return unlink(OTTD2FS(filename)) == 0;
}
bool FileExists(const char *filename)
{
return access(filename, 0) == 0;
}
typedef byte fios_getlist_callback_proc(int mode, const char *filename, const char *ext, char *title);
/** Create a list of the files in a directory, according to some arbitrary rule.
* @param num Will be filled with the amount of items.
* @param mode The mode we are in. Some modes don't allow 'parent'.
* @param callback The function that is called where you need to do the filtering.
* @return Return the list of files. */
static FiosItem *FiosGetFileList(int mode, fios_getlist_callback_proc *callback_proc)
{
struct stat sb;
struct dirent *dirent;
DIR *dir;
FiosItem *fios;
int sort_start;
char d_name[sizeof(fios->name)];
/* A parent directory link exists if we are not in the root directory */
if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
fios = FiosAlloc();
fios->type = FIOS_TYPE_PARENT;
fios->mtime = 0;
ttd_strlcpy(fios->name, "..", lengthof(fios->name));
ttd_strlcpy(fios->title, ".. (Parent directory)", lengthof(fios->title));
}
/* Show subdirectories */
if (mode != SLD_NEW_GAME && (dir = ttd_opendir(_fios_path)) != NULL) {
while ((dirent = readdir(dir)) != NULL) {
ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name));
/* found file must be directory, but not '.' or '..' */
if (FiosIsValidFile(_fios_path, dirent, &sb) && (sb.st_mode & S_IFDIR) &&
(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
fios = FiosAlloc();
fios->type = FIOS_TYPE_DIR;
fios->mtime = 0;
ttd_strlcpy(fios->name, d_name, lengthof(fios->name));
snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
str_validate(fios->title);
}
}
closedir(dir);
}
/* Sort the subdirs always by name, ascending, remember user-sorting order */
{
byte order = _savegame_sort_order;
_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
qsort(_fios_items, _fios_count, sizeof(FiosItem), compare_FiosItems);
_savegame_sort_order = order;
}
/* This is where to start sorting for the filenames */
sort_start = _fios_count;
/* Show files */
dir = ttd_opendir(_fios_path);
if (dir != NULL) {
while ((dirent = readdir(dir)) != NULL) {
char fios_title[64];
char *t;
byte type;
ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name));
if (!FiosIsValidFile(_fios_path, dirent, &sb) || !(sb.st_mode & S_IFREG) || FiosIsHiddenFile(dirent)) continue;
/* File has no extension, skip it */
if ((t = strrchr(d_name, '.')) == NULL) continue;
fios_title[0] = '\0'; // reset the title;
type = callback_proc(mode, d_name, t, fios_title);
if (type != FIOS_TYPE_INVALID) {
fios = FiosAlloc();
fios->mtime = sb.st_mtime;
fios->type = type;
ttd_strlcpy(fios->name, d_name, lengthof(fios->name));
/* Some callbacks want to lookup the title of the file. Allow that.
* If we just copy the title from the filename, strip the extension */
t = (fios_title[0] == '\0') ? *t = '\0', d_name : fios_title;
ttd_strlcpy(fios->title, t, lengthof(fios->title));
str_validate(fios->title);
}
}
closedir(dir);
}
qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems);
/* Show drives */
if (mode != SLD_NEW_GAME) FiosGetDrives();
_fios_num = _fios_count;
return _fios_items;
}
/**
* Callback for FiosGetFileList. It tells if a file is a savegame or not.
* @param mode Save/load mode.
* @param file Name of the file to check.
* @param ext A pointer to the extension identifier inside file
* @param title Buffer if a callback wants to lookup the title of the file
* @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a savegame
* @see FiosGetFileList
* @see FiosGetSavegameList
*/
static byte FiosGetSavegameListCallback(int mode, const char *file, const char *ext, char *title)
{
/* Show savegame files
* .SAV OpenTTD saved game
* .SS1 Transport Tycoon Deluxe preset game
* .SV1 Transport Tycoon Deluxe (Patch) saved game
* .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
if (strcasecmp(ext, ".sav") == 0) return FIOS_TYPE_FILE;
if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
strcasecmp(ext, ".sv2") == 0) {
GetOldSaveGameName(title, _fios_path, file);
return FIOS_TYPE_OLDFILE;
}
}
return FIOS_TYPE_INVALID;
}
/**
* Get a list of savegames.
* @param mode Save/load mode.
* @return A pointer to an array of FiosItem representing all the files to be shown in the save/load dialog.
* @see FiosGetFileList
*/
FiosItem *FiosGetSavegameList(int mode)
{
static char *_fios_save_path = NULL;
if (_fios_save_path == NULL) {
_fios_save_path = malloc(MAX_PATH);
ttd_strlcpy(_fios_save_path, _paths.save_dir, MAX_PATH);
}
_fios_path = _fios_save_path;
return FiosGetFileList(mode, &FiosGetSavegameListCallback);
}
/**
* Callback for FiosGetFileList. It tells if a file is a scenario or not.
* @param mode Save/load mode.
* @param file Name of the file to check.
* @param ext A pointer to the extension identifier inside file
* @param title Buffer if a callback wants to lookup the title of the file
* @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a scenario
* @see FiosGetFileList
* @see FiosGetScenarioList
*/
static byte FiosGetScenarioListCallback(int mode, const char *file, const char *ext, char *title)
{
/* Show scenario files
* .SCN OpenTTD style scenario file
* .SV0 Transport Tycoon Deluxe (Patch) scenario
* .SS0 Transport Tycoon Deluxe preset scenario */
if (strcasecmp(ext, ".scn") == 0) return FIOS_TYPE_SCENARIO;
if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
GetOldSaveGameName(title, _fios_path, file);
return FIOS_TYPE_OLD_SCENARIO;
}
}
return FIOS_TYPE_INVALID;
}
/**
* Get a list of scenarios.
* @param mode Save/load mode.
* @return A pointer to an array of FiosItem representing all the files to be shown in the save/load dialog.
* @see FiosGetFileList
*/
FiosItem *FiosGetScenarioList(int mode)
{
static char *_fios_scn_path = NULL;
if (_fios_scn_path == NULL) {
_fios_scn_path = malloc(MAX_PATH);
ttd_strlcpy(_fios_scn_path, _paths.scenario_dir, MAX_PATH);
}
_fios_path = _fios_scn_path;
return FiosGetFileList(mode, &FiosGetScenarioListCallback);
}
static byte FiosGetHeightmapListCallback(int mode, const char *file, const char *ext, char *title)
{
/* Show heightmap files
* .PNG PNG Based heightmap files
* .BMP BMP Based heightmap files
*/
#ifdef WITH_PNG
if (strcasecmp(ext, ".png") == 0) return FIOS_TYPE_PNG;
#endif /* WITH_PNG */
if (strcasecmp(ext, ".bmp") == 0) return FIOS_TYPE_BMP;
return FIOS_TYPE_INVALID;
}
// Get a list of Heightmaps
FiosItem *FiosGetHeightmapList(int mode)
{
static char *_fios_hmap_path = NULL;
if (_fios_hmap_path == NULL) {
_fios_hmap_path = malloc(MAX_PATH);
strcpy(_fios_hmap_path, _paths.heightmap_dir);
}
_fios_path = _fios_hmap_path;
return FiosGetFileList(mode, &FiosGetHeightmapListCallback);
}

101
fios.h
View File

@@ -1,101 +0,0 @@
/* $Id$ */
#ifndef FIOS_H
#define FIOS_H
/* Deals with finding savegames */
typedef struct {
byte type;
uint64 mtime;
char title[64];
char name[256 - 12 - 64];
} FiosItem;
enum {
FIOS_TYPE_DRIVE = 0,
FIOS_TYPE_PARENT = 1,
FIOS_TYPE_DIR = 2,
FIOS_TYPE_FILE = 3,
FIOS_TYPE_OLDFILE = 4,
FIOS_TYPE_SCENARIO = 5,
FIOS_TYPE_OLD_SCENARIO = 6,
FIOS_TYPE_DIRECT = 7,
FIOS_TYPE_PNG = 8,
FIOS_TYPE_BMP = 9,
FIOS_TYPE_INVALID = 255,
};
/* Variables to display file lists */
extern FiosItem *_fios_list; // defined in misc_gui.c
extern int _fios_num; // defined in fios.c, read_only version of _fios_count
extern int _saveload_mode; // defined in misc_gui.c
// Get a list of savegames
FiosItem *FiosGetSavegameList(int mode);
// Get a list of scenarios
FiosItem *FiosGetScenarioList(int mode);
// Get a list of Heightmaps
FiosItem *FiosGetHeightmapList(int mode);
// Free the list of savegames
void FiosFreeSavegameList(void);
// Browse to. Returns a filename w/path if we reached a file.
char *FiosBrowseTo(const FiosItem *item);
// Return path, free space and stringID
StringID FiosGetDescText(const char **path, uint32 *total_free);
// Delete a name
bool FiosDelete(const char *name);
// Make a filename from a name
void FiosMakeSavegameName(char *buf, const char *name, size_t size);
// Allocate a new FiosItem
FiosItem *FiosAlloc(void);
int CDECL compare_FiosItems(const void *a, const void *b);
/* Implementation of opendir/readdir/closedir for Windows */
#if defined(WIN32)
#include <windows.h>
typedef struct DIR DIR;
typedef struct dirent { // XXX - only d_name implemented
TCHAR *d_name; /* name of found file */
/* little hack which will point to parent DIR struct which will
* save us a call to GetFileAttributes if we want information
* about the file (for example in function fio_bla */
DIR *dir;
} dirent;
struct DIR {
HANDLE hFind;
/* the dirent returned by readdir.
* note: having only one global instance is not possible because
* multiple independent opendir/readdir sequences must be supported. */
dirent ent;
WIN32_FIND_DATA fd;
/* since opendir calls FindFirstFile, we need a means of telling the
* first call to readdir that we already have a file.
* that's the case iff this is true */
bool at_first_entry;
};
DIR *opendir(const TCHAR *path);
struct dirent *readdir(DIR *d);
int closedir(DIR *d);
#else
/* Use system-supplied opendir/readdir/closedir functions */
# include <sys/types.h>
# include <dirent.h>
#endif /* defined(WIN32) */
/**
* A wrapper around opendir() which will convert the string from
* OPENTTD encoding to that of the filesystem. For all purposes this
* function behaves the same as the original opendir function
* @param path string to open directory of
* @return DIR pointer
*/
static inline DIR *ttd_opendir(const char *path)
{
return opendir(OTTD2FS(path));
}
#endif /* FIOS_H */

View File

@@ -1,522 +0,0 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "functions.h"
#include "macros.h"
#include "debug.h"
#include "table/sprites.h"
#include "table/control_codes.h"
#include "spritecache.h"
#include "gfx.h"
#include "string.h"
#include "fontcache.h"
#ifdef WITH_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#ifdef WITH_FONTCONFIG
#include <fontconfig/fontconfig.h>
#endif
static FT_Library _library = NULL;
static FT_Face _face_small = NULL;
static FT_Face _face_medium = NULL;
static FT_Face _face_large = NULL;
FreeTypeSettings _freetype;
enum {
FACE_COLOUR = 1,
SHADOW_COLOUR = 2,
};
/** Get the font loaded into a Freetype face by using a font-name.
* If no appropiate font is found, the function returns an error */
#ifdef WIN32
#include <windows.h>
#include <tchar.h>
#include <shlobj.h> // SHGetFolderPath
#include "win32.h"
/* Get the font file to be loaded into Freetype by looping the registry
* location where windows lists all installed fonts. Not very nice, will
* surely break if the registry path changes, but it works. Much better
* solution would be to use CreateFont, and extract the font data from it
* by GetFontData. The problem with this is that the font file needs to be
* kept in memory then until the font is no longer needed. This could mean
* an additional memory usage of 30MB (just for fonts!) when using an eastern
* font for all font sizes */
#define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
#define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
{
FT_Error err = FT_Err_Cannot_Open_Resource;
HKEY hKey;
LONG ret;
TCHAR vbuffer[MAX_PATH], dbuffer[256];
TCHAR *font_namep;
char *font_path;
uint index;
/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
* "Windows NT" key, on Windows 9x in the Windows key. To save us having
* to retrieve the windows version, we'll just query both */
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
if (ret != ERROR_SUCCESS) {
DEBUG(freetype, 0) ("Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
return err;
}
/* For Unicode we need some conversion between widechar and
* normal char to match the data returned by RegEnumValue,
* otherwise just use parameter */
#if defined(UNICODE)
font_namep = malloc(MAX_PATH * sizeof(TCHAR));
MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR));
#else
font_namep = (char*)font_name; // only cast because in unicode pointer is not const
#endif
for (index = 0;; index++) {
TCHAR *s;
DWORD vbuflen = lengthof(vbuffer);
DWORD dbuflen = lengthof(dbuffer);
ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
if (ret != ERROR_SUCCESS) goto registry_no_font_found;
/* The font names in the registry are of the following 3 forms:
* - ADMUI3.fon
* - Book Antiqua Bold (TrueType)
* - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
* We will strip the font-type '()' if any and work with the font name
* itself, which must match exactly; if...
* TTC files, font files which contain more than one font are seperated
* byt '&'. Our best bet will be to do substr match for the fontname
* and then let FreeType figure out which index to load */
s = _tcschr(vbuffer, _T('('));
if (s != NULL) s[-1] = '\0';
if (_tcschr(vbuffer, _T('&')) == NULL) {
if (_tcsicmp(vbuffer, font_namep) == 0) break;
} else {
if (_tcsstr(vbuffer, font_namep) != NULL) break;
}
}
if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
DEBUG(freetype, 0) ("SHGetFolderPath cannot return fonts directory");
goto folder_error;
}
/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
* contain multiple fonts inside this single file. GetFontData however
* returns the whole file, so we need to check each font inside to get the
* proper font.
* Also note that FreeType does not support UNICODE filesnames! */
#if defined(UNICODE)
/* We need a cast here back from wide because FreeType doesn't support
* widechar filenames. Just use the buffer we allocated before for the
* font_name search */
font_path = (char*)font_namep;
WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR));
#else
font_path = vbuffer;
#endif
ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR));
ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR));
index = 0;
do {
err = FT_New_Face(_library, font_path, index, face);
if (err != FT_Err_Ok) break;
if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
err = FT_Err_Cannot_Open_Resource;
} while ((FT_Long)++index != (*face)->num_faces);
folder_error:
registry_no_font_found:
#if defined(UNICODE)
free(font_namep);
#endif
RegCloseKey(hKey);
return err;
}
#else
# ifdef WITH_FONTCONFIG
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
{
FT_Error err = FT_Err_Cannot_Open_Resource;
if (!FcInit()) {
ShowInfoF("Unable to load font configuration");
} else {
FcPattern *match;
FcPattern *pat;
FcFontSet *fs;
FcResult result;
char *font_style;
char *font_family;
/* Split & strip the font's style */
font_family = strdup(font_name);
font_style = strchr(font_family, ',');
if (font_style != NULL) {
font_style[0] = '\0';
font_style++;
while (*font_style == ' ' || *font_style == '\t') font_style++;
}
/* Resolve the name and populate the information structure */
pat = FcNameParse((FcChar8*)font_family);
if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
FcConfigSubstitute(0, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
fs = FcFontSetCreate();
match = FcFontMatch(0, pat, &result);
if (fs != NULL && match != NULL) {
int i;
FcChar8 *family;
FcChar8 *style;
FcChar8 *file;
FcFontSetAdd(fs, match);
for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
/* Try the new filename */
if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
/* The correct style? */
if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
/* Font config takes the best shot, which, if the family name is spelled
* wrongly a 'random' font, so check whether the family name is the
* same as the supplied name */
if (strcasecmp(font_family, (char*)family) == 0) {
err = FT_New_Face(_library, (char *)file, 0, face);
}
}
}
}
free(font_family);
FcPatternDestroy(pat);
FcFontSetDestroy(fs);
FcFini();
}
return err;
}
# else
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
# endif /* WITH_FONTCONFIG */
#endif
/**
* Loads the freetype font.
* First type to load the fontname as if it were a path. If that fails,
* try to resolve the filename of the font using fontconfig, where the
* format is 'font family name' or 'font family name, font style'.
*/
static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type)
{
FT_Error error;
if (strlen(font_name) == 0) return;
error = FT_New_Face(_library, font_name, 0, face);
if (error != FT_Err_Ok) error = GetFontByFaceName(font_name, face);
if (error == FT_Err_Ok) {
DEBUG(freetype, 2) ("[FreeType] Requested '%s', using '%s %s'", font_name, (*face)->family_name, (*face)->style_name);
/* Attempt to select the unicode character map */
error = FT_Select_Charmap(*face, ft_encoding_unicode);
if (error == FT_Err_Ok) return; // Success
if (error == FT_Err_Invalid_CharMap_Handle) {
/* Try to pick a different character map instead. We default to
* the first map, but platform_id 0 encoding_id 0 should also
* be unicode (strange system...) */
FT_CharMap found = (*face)->charmaps[0];
int i;
for (i = 0; i < (*face)->num_charmaps; i++) {
FT_CharMap charmap = (*face)->charmaps[i];
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
found = charmap;
}
}
if (found != NULL) {
error = FT_Set_Charmap(*face, found);
if (error == FT_Err_Ok) return;
}
}
}
FT_Done_Face(*face);
*face = NULL;
ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
}
void InitFreeType(void)
{
if (strlen(_freetype.small_font) == 0 && strlen(_freetype.medium_font) == 0 && strlen(_freetype.large_font) == 0) {
DEBUG(freetype, 1) ("[FreeType] No font faces specified, using sprite fonts instead");
return;
}
if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
return;
}
DEBUG(freetype, 2) ("[FreeType] Initialized");
/* Load each font */
LoadFreeTypeFont(_freetype.small_font, &_face_small, "small");
LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium");
LoadFreeTypeFont(_freetype.large_font, &_face_large, "large");
/* Set each font size */
if (_face_small != NULL) FT_Set_Pixel_Sizes(_face_small, 0, _freetype.small_size);
if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size);
if (_face_large != NULL) FT_Set_Pixel_Sizes(_face_large, 0, _freetype.large_size);
}
static FT_Face GetFontFace(FontSize size)
{
switch (size) {
default: NOT_REACHED();
case FS_NORMAL: return _face_medium;
case FS_SMALL: return _face_small;
case FS_LARGE: return _face_large;
}
}
typedef struct GlyphEntry {
Sprite *sprite;
byte width;
} GlyphEntry;
/* The glyph cache. This is structured to reduce memory consumption.
* 1) There is a 'segment' table for each font size.
* 2) Each segment table is a discrete block of characters.
* 3) Each block contains 256 (aligned) characters sequential characters.
*
* The cache is accessed in the following way:
* For character 0x0041 ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
* For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
*
* Currently only 256 segments are allocated, "limiting" us to 65536 characters.
* This can be simply changed in the two functions Get & SetGlyphPtr.
*/
static GlyphEntry **_glyph_ptr[FS_END];
static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
{
if (_glyph_ptr[size] == NULL) return NULL;
if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
}
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
{
if (_glyph_ptr[size] == NULL) {
DEBUG(freetype, 3) ("[FreeType] Allocating root glyph cache for size %u", size);
_glyph_ptr[size] = calloc(256, sizeof(**_glyph_ptr));
}
if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
DEBUG(freetype, 3) ("[FreeType] Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
_glyph_ptr[size][GB(key, 8, 8)] = calloc(256, sizeof(***_glyph_ptr));
}
DEBUG(freetype, 4) ("[FreeType] Set glyph for unicode character 0x%04X, size %u", key, size);
_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width = glyph->width;
}
const Sprite *GetGlyph(FontSize size, WChar key)
{
FT_Face face = GetFontFace(size);
FT_GlyphSlot slot;
GlyphEntry new_glyph;
GlyphEntry *glyph;
Sprite *sprite;
int width;
int height;
int x;
int y;
int y_adj;
assert(IsPrintable(key));
/* Bail out if no face loaded, or for our special characters */
if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
SpriteID sprite = GetUnicodeGlyph(size, key);
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
return GetSprite(sprite);
}
/* Check for the glyph in our cache */
glyph = GetGlyphPtr(size, key);
if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
slot = face->glyph;
FT_Load_Char(face, key, FT_LOAD_DEFAULT);
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
width = max(1, slot->bitmap.width + (size == FS_NORMAL));
height = max(1, slot->bitmap.rows + (size == FS_NORMAL));
/* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
sprite = calloc(width * height + 8, 1);
sprite->info = 1;
sprite->width = width;
sprite->height = height;
sprite->x_offs = slot->bitmap_left;
// XXX 2 should be determined somehow... it's right for the normal face
y_adj = (size == FS_NORMAL) ? 2 : 0;
sprite->y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj;
/* Draw shadow for medium size */
if (size == FS_NORMAL) {
for (y = 0; y < slot->bitmap.rows; y++) {
for (x = 0; x < slot->bitmap.width; x++) {
if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
sprite->data[1 + x + (1 + y) * sprite->width] = SHADOW_COLOUR;
}
}
}
}
for (y = 0; y < slot->bitmap.rows; y++) {
for (x = 0; x < slot->bitmap.width; x++) {
if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
sprite->data[x + y * sprite->width] = FACE_COLOUR;
}
}
}
new_glyph.sprite = sprite;
new_glyph.width = (slot->advance.x >> 6) + (size != FS_NORMAL);
SetGlyphPtr(size, key, &new_glyph);
return sprite;
}
uint GetGlyphWidth(FontSize size, WChar key)
{
FT_Face face = GetFontFace(size);
GlyphEntry *glyph;
if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
SpriteID sprite = GetUnicodeGlyph(size, key);
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
}
glyph = GetGlyphPtr(size, key);
if (glyph == NULL || glyph->sprite == NULL) {
GetGlyph(size, key);
glyph = GetGlyphPtr(size, key);
}
return glyph->width;
}
#endif /* WITH_FREETYPE */
/* Sprite based glyph mapping */
#include "table/unicode.h"
static SpriteID **_unicode_glyph_map[FS_END];
/** Get the SpriteID of the first glyph for the given font size */
static SpriteID GetFontBase(FontSize size)
{
switch (size) {
default: NOT_REACHED();
case FS_NORMAL: return SPR_ASCII_SPACE;
case FS_SMALL: return SPR_ASCII_SPACE_SMALL;
case FS_LARGE: return SPR_ASCII_SPACE_BIG;
}
}
SpriteID GetUnicodeGlyph(FontSize size, uint32 key)
{
if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0;
return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)];
}
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite)
{
if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = calloc(256, sizeof(*_unicode_glyph_map[size]));
if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = calloc(256, sizeof(**_unicode_glyph_map[size]));
_unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite;
}
void InitializeUnicodeGlyphMap(void)
{
FontSize size;
SpriteID base;
SpriteID sprite;
uint i;
for (size = FS_NORMAL; size != FS_END; size++) {
/* Clear out existing glyph map if it exists */
if (_unicode_glyph_map[size] != NULL) {
for (i = 0; i < 256; i++) {
if (_unicode_glyph_map[size][i] != NULL) free(_unicode_glyph_map[size][i]);
}
free(_unicode_glyph_map[size]);
_unicode_glyph_map[size] = NULL;
}
base = GetFontBase(size);
for (i = ASCII_LETTERSTART; i < 256; i++) {
sprite = base + i - ASCII_LETTERSTART;
if (!SpriteExists(sprite)) continue;
SetUnicodeGlyph(size, i, sprite);
SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite);
}
for (i = 0; i < lengthof(_default_unicode_map); i++) {
sprite = base + _default_unicode_map[i].key - ASCII_LETTERSTART;
SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite);
}
}
}

View File

@@ -1,56 +0,0 @@
/* $Id$ */
#ifndef FONTCACHE_H
#define FONTCACHE_H
/** Get the SpriteID mapped to the given font size and key */
SpriteID GetUnicodeGlyph(FontSize size, uint32 key);
/** Map a SpriteID to the font size and key */
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite);
/** Initialize the glyph map */
void InitializeUnicodeGlyphMap(void);
#ifdef WITH_FREETYPE
typedef struct FreeTypeSettings {
char small_font[260];
char medium_font[260];
char large_font[260];
uint small_size;
uint medium_size;
uint large_size;
} FreeTypeSettings;
extern FreeTypeSettings _freetype;
void InitFreeType(void);
const struct Sprite *GetGlyph(FontSize size, uint32 key);
uint GetGlyphWidth(FontSize size, uint32 key);
#else
/* Stub for initializiation */
static inline void InitFreeType(void) {}
/** Get the Sprite for a glyph */
static inline const Sprite *GetGlyph(FontSize size, uint32 key)
{
SpriteID sprite = GetUnicodeGlyph(size, key);
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
return GetSprite(sprite);
}
/** Get the width of a glyph */
static inline uint GetGlyphWidth(FontSize size, uint32 key)
{
SpriteID sprite = GetUnicodeGlyph(size, key);
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
}
#endif /* WITH_FREETYPE */
#endif /* FONTCACHE_H */

View File

@@ -3,10 +3,15 @@
#ifndef FUNCTIONS_H
#define FUNCTIONS_H
/* landscape.c */
void FindLandscapeHeight(TileInfo *ti, uint x, uint y);
void FindLandscapeHeightByTile(TileInfo *ti, TileIndex tile);
void DoClearSquare(TileIndex tile);
void CDECL ModifyTile(TileIndex tile, uint flags, ...);
void RunTileLoop(void);
uint GetPartialZ(int x, int y, Slope corners);
uint GetPartialZ(int x, int y, int corners);
uint GetSlopeZ(int x, int y);
uint32 GetTileTrackStatus(TileIndex tile, TransportType mode);
void GetAcceptedCargo(TileIndex tile, AcceptedCargo ac);
@@ -14,6 +19,7 @@ void ChangeTileOwner(TileIndex tile, byte old_player, byte new_player);
void AnimateTile(TileIndex tile);
void ClickTile(TileIndex tile);
void GetTileDesc(TileIndex tile, TileDesc *td);
void DrawTile(TileInfo *ti);
void UpdateTownMaxPass(Town *t);
bool IsValidTile(TileIndex tile);
@@ -44,6 +50,9 @@ void DrawClearLandTile(const TileInfo *ti, byte set);
void DrawClearLandFence(const TileInfo *ti);
void TileLoopClearHelper(TileIndex tile);
/* road_land.c */
void DrawRoadDepotSprite(int x, int y, int image);
/* water_land.c */
void DrawShipDepotSprite(int x, int y, int image);
void TileLoop_Water(TileIndex tile);
@@ -52,7 +61,7 @@ void TileLoop_Water(TileIndex tile);
bool CheckPlayerHasMoney(int32 cost);
void SubtractMoneyFromPlayer(int32 cost);
void SubtractMoneyFromPlayerFract(PlayerID player, int32 cost);
bool CheckOwnership(Owner owner);
bool CheckOwnership(PlayerID owner);
bool CheckTileOwnership(TileIndex tile);
StringID GetPlayerNameString(PlayerID player, uint index);
@@ -63,15 +72,15 @@ void NORETURN CDECL error(const char *str, ...);
/* openttd.c */
/**************
* Warning: DO NOT enable this unless you understand what it does
*
* If enabled, in a network game all randoms will be dumped to the
* stdout if the first client joins (or if you are a client). This
* is to help finding desync problems.
*
* Warning: DO NOT enable this unless you understand what it does
**************/
// **************
// * Warning: DO NOT enable this unless you understand what it does
// *
// * If enabled, in a network game all randoms will be dumped to the
// * stdout if the first client joins (or if you are a client). This
// * is to help finding desync problems.
// *
// * Warning: DO NOT enable this unless you understand what it does
// **************
//#define RANDOM_DEBUG
@@ -108,6 +117,13 @@ static inline TileIndex RandomTile(void) { return TILE_MASK(Random()); }
uint32 InteractiveRandom(void); /* Used for random sequences that are not the same on the other end of the multiplayer link */
uint InteractiveRandomRange(uint max);
// Used for profiling
#define TIC() { extern uint32 _rdtsc(void); uint32 _xxx_ = _rdtsc(); static float __avg__;
#define TOC(s) _xxx_ = _rdtsc() - _xxx_; __avg__=__avg__*0.99+_xxx_*0.01; printf("%s: %8d %f\n", s, _xxx_,__avg__); }
void SetDate(uint date);
/* facedraw.c */
void DrawPlayerFace(uint32 face, int color, int x, int y);
@@ -121,6 +137,7 @@ void InitTextMessage(void);
void DrawTextMessage(void);
void CDECL AddTextMessage(uint16 color, uint8 duration, const char *message, ...);
void UndrawTextMessage(void);
void TextMessageDailyLoop(void);
bool AddAnimatedTile(TileIndex tile);
void DeleteAnimatedTile(TileIndex tile);
@@ -128,26 +145,43 @@ void AnimateAnimatedTiles(void);
void InitializeAnimatedTiles(void);
/* tunnelbridge_cmd.c */
bool CheckTunnelInWay(TileIndex tile, int z);
bool CheckBridge_Stuff(byte bridge_type, uint bridge_len);
uint32 GetBridgeLength(TileIndex begin, TileIndex end);
int CalcBridgeLenCostFactor(int x);
typedef void CommandCallback(bool success, TileIndex tile, uint32 p1, uint32 p2);
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd);
/* network.c */
void NetworkUDPClose(void);
void NetworkStartUp(void);
void NetworkShutDown(void);
void NetworkGameLoop(void);
void NetworkUDPGameLoop(void);
bool NetworkServerStart(void);
bool NetworkClientConnectGame(const char* host, unsigned short port);
void NetworkReboot(void);
void NetworkDisconnect(void);
void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback);
/* misc_cmd.c */
void PlaceTreesRandomly(void);
void InitializeLandscapeVariables(bool only_constants);
/* misc.c */
bool IsCustomName(StringID id);
void DeleteName(StringID id);
char *GetName(char *buff, StringID id, const char* last);
char *GetName(int id, char *buff);
// AllocateNameUnique also tests if the name used is not used anywere else
// and if it is used, it returns an error.
#define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true)
#define AllocateName(name, skip) RealAllocateName(name, skip, false)
StringID RealAllocateName(const char *name, byte skip, bool check_double);
void ConvertNameArray(void);
void ConvertDayToYMD(YearMonthDay *ymd, uint16 date);
uint ConvertYMDToDay(uint year, uint month, uint day);
uint ConvertIntDate(uint date);
/* misc functions */
void MarkTileDirty(int x, int y);
@@ -155,7 +189,6 @@ void MarkTileDirtyByTile(TileIndex tile);
void InvalidateWindow(WindowClass cls, WindowNumber number);
void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index);
void InvalidateWindowClasses(WindowClass cls);
void InvalidateWindowClassesData(WindowClass cls);
void DeleteWindowById(WindowClass cls, WindowNumber number);
void DeleteWindowByClass(WindowClass cls);
@@ -169,8 +202,9 @@ bool ScrollWindowTo(int x, int y, Window * w);
bool ScrollMainWindowToTile(TileIndex tile);
bool ScrollMainWindowTo(int x, int y);
void DrawSprite(uint32 img, int x, int y);
uint GetCorrectTileHeight(TileIndex tile);
bool EnsureNoVehicle(TileIndex tile);
bool EnsureNoVehicleOnGround(TileIndex tile);
bool EnsureNoVehicleZ(TileIndex tile, byte z);
void MarkAllViewportsDirty(int left, int top, int right, int bottom);
void ShowCostOrIncomeAnimation(int x, int y, int z, int32 cost);
void ShowFeederIncomeAnimation(int x, int y, int z, int32 cost);
@@ -181,13 +215,20 @@ bool CheckIfAuthorityAllows(TileIndex tile);
Town *ClosestTownFromTile(TileIndex tile, uint threshold);
void ChangeTownRating(Town *t, int add, int max);
uint GetTownRadiusGroup(const Town* t, TileIndex tile);
uint GetRoadBitsByTile(TileIndex tile);
int GetTownRadiusGroup(const Town *t, TileIndex tile);
void ShowNetworkChatQueryWindow(byte desttype, byte dest);
void ShowNetworkGiveMoneyWindow(byte player);
void ShowNetworkNeedGamePassword(void);
void ShowNetworkNeedCompanyPassword(void);
int FindFirstBit(uint32 x);
void ShowHighscoreTable(int difficulty, int8 rank);
void ShowEndGameChart(void);
TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng);
void AfterLoadTown(void);
void UpdatePatches(void);
void GenRandomNewGame(uint32 rnd1, uint32 rnd2);
void StartScenarioEditor(uint32 rnd1, uint32 rnd2);
void AskExitGame(void);
void AskExitToGameMenu(void);
@@ -198,12 +239,11 @@ StringID RemapOldStringID(StringID s);
void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str);
enum {
SLD_LOAD_GAME,
SLD_LOAD_SCENARIO,
SLD_SAVE_GAME,
SLD_SAVE_SCENARIO,
SLD_LOAD_HEIGHTMAP,
SLD_NEW_GAME,
SLD_LOAD_GAME = 0,
SLD_LOAD_SCENARIO = 1,
SLD_SAVE_GAME = 2,
SLD_SAVE_SCENARIO = 3,
SLD_NEW_GAME = 4,
};
void ShowSaveLoadDialog(int mode);
@@ -212,16 +252,16 @@ void GameSizeChanged(void);
bool FileExists(const char *filename);
bool ReadLanguagePack(int index);
void InitializeLanguagePacks(void);
const char *GetCurrentLocale(const char *param);
void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize);
int GetLanguageList(char **languages, int max);
void LoadFromConfig(void);
void SaveToConfig(void);
void CheckConfig(void);
int ttd_main(int argc, char* argv[]);
void HandleExitGameRequest(void);
void DeterminePaths(void);
void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
void CSleep(int milliseconds);
#endif /* FUNCTIONS_H */

View File

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

View File

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

View File

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

1448
gfx.c

File diff suppressed because it is too large Load Diff

92
gfx.h
View File

@@ -5,6 +5,13 @@
typedef byte Pixel;
typedef struct ColorList {
byte unk0, unk1, unk2;
byte window_color_1a, window_color_1b;
byte window_color_bga, window_color_bgb;
byte window_color_2;
} ColorList;
struct DrawPixelInfo {
Pixel *dst_ptr;
int left, top, width, height;
@@ -14,37 +21,28 @@ struct DrawPixelInfo {
typedef struct CursorVars {
Point pos, size, offs, delta; ///< position, size, offset from top-left, and movement
Point draw_pos, draw_size; ///< position and size bounding-box for drawing
CursorID sprite; ///< current image of cursor
Point pos, size, offs, delta;
Point draw_pos, draw_size;
CursorID sprite;
int wheel; ///< mouse wheel movement
const CursorID *animate_list, *animate_cur; ///< in case of animated cursor, list of frames
uint animate_timeout; ///< current frame in list of animated cursor
int wheel; // mouse wheel movement
const CursorID *animate_list, *animate_cur;
uint animate_timeout;
bool visible; ///< cursor is visible
bool dirty; ///< the rect occupied by the mouse is dirty (redraw)
bool fix_at; ///< mouse is moving, but cursor is not (used for scrolling)
bool in_window; ///< mouse inside this window, determines drawing logic
bool visible;
bool dirty;
bool fix_at;
} CursorVars;
typedef enum FontSizes {
FS_NORMAL,
FS_SMALL,
FS_LARGE,
FS_END,
} FontSize;
void RedrawScreenRect(int left, int top, int right, int bottom);
void GfxScroll(int left, int top, int width, int height, int xo, int yo);
// XXX doesn't really belong here, but the only
// consumers always use it in conjunction with DoDrawString()
#define UPARROW "\xEE\x8A\x80"
#define DOWNARROW "\xEE\x8A\xAA"
#define UPARROW "\x80"
#define DOWNARROW "\xAA"
int DrawStringCentered(int x, int y, StringID str, uint16 color);
@@ -60,25 +58,25 @@ int DoDrawStringTruncated(const char *str, int x, int y, uint16 color, uint maxw
void DrawStringCenterUnderline(int x, int y, StringID str, uint16 color);
void DrawStringCenterUnderlineTruncated(int xl, int xr, int y, StringID str, uint16 color);
int DrawStringRightAligned(int x, int y, StringID str, uint16 color);
void DrawStringRightAligned(int x, int y, StringID str, uint16 color);
void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, uint maxw);
void DrawStringRightAlignedUnderline(int x, int y, StringID str, uint16 color);
void GfxFillRect(int left, int top, int right, int bottom, int color);
void GfxDrawLine(int left, int top, int right, int bottom, int color);
void DrawFrameRect(int left, int top, int right, int bottom, int color, int flags);
uint16 GetDrawStringPlayerColor(PlayerID player);
BoundingRect GetStringBoundingBox(const char *str);
uint32 FormatStringLinebreaks(char *str, int maxw);
int GetStringWidth(const char *str);
void LoadStringWidthTable(void);
void DrawStringMultiCenter(int x, int y, StringID str, int maxw);
uint DrawStringMultiLine(int x, int y, StringID str, int maxw);
void DrawStringMultiLine(int x, int y, StringID str, int maxw);
void DrawDirtyBlocks(void);
void SetDirtyBlocks(int left, int top, int right, int bottom);
void MarkWholeScreenDirty(void);
void GfxInitPalettes(void);
bool FillDrawPixelInfo(DrawPixelInfo* n, int left, int top, int width, int height);
bool FillDrawPixelInfo(DrawPixelInfo* n, const DrawPixelInfo* o, int left, int top, int width, int height);
/* window.c */
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom);
@@ -95,49 +93,19 @@ void ToggleFullScreen(bool fs);
/* gfx.c */
#define ASCII_LETTERSTART 32
extern FontSize _cur_fontsize;
byte GetCharacterWidth(FontSize size, uint32 key);
static inline byte GetCharacterHeight(FontSize size)
VARDEF int _stringwidth_base;
VARDEF byte _stringwidth_table[0x2A0];
static inline byte GetCharacterWidth(uint key)
{
switch (size) {
default: NOT_REACHED();
case FS_NORMAL: return 10;
case FS_SMALL: return 6;
case FS_LARGE: return 18;
}
assert(key >= ASCII_LETTERSTART && key - ASCII_LETTERSTART < lengthof(_stringwidth_table));
return _stringwidth_table[key - ASCII_LETTERSTART];
}
VARDEF DrawPixelInfo _screen;
VARDEF DrawPixelInfo *_cur_dpi;
VARDEF ColorList _color_list[16];
VARDEF CursorVars _cursor;
enum {
COLOUR_DARK_BLUE,
COLOUR_PALE_GREEN,
COLOUR_PINK,
COLOUR_YELLOW,
COLOUR_RED,
COLOUR_LIGHT_BLUE,
COLOUR_GREEN,
COLOUR_DARK_GREEN,
COLOUR_BLUE,
COLOUR_CREAM,
COLOUR_MAUVE,
COLOUR_PURPLE,
COLOUR_ORANGE,
COLOUR_BROWN,
COLOUR_GREY,
COLOUR_WHITE
};
/**
* All 16 colour gradients
* 8 colours per gradient from darkest (0) to lightest (7)
*/
VARDEF byte _colour_gradient[16][8];
VARDEF int _pal_first_dirty;
VARDEF int _pal_last_dirty;

View File

@@ -9,12 +9,10 @@
#include "spritecache.h"
#include "table/sprites.h"
#include "fileio.h"
#include "string.h"
#include "newgrf.h"
#include "md5.h"
#include "variables.h"
#include "fontcache.h"
#include <string.h>
#include <ctype.h>
typedef struct MD5File {
const char * const filename; // filename
@@ -22,7 +20,7 @@ typedef struct MD5File {
} MD5File;
typedef struct FileList {
const MD5File basic[4]; // grf files that always have to be loaded
const MD5File basic[5]; // grf files that always have to be loaded
const MD5File landscape[3]; // landscape specific grf files
} FileList;
@@ -98,9 +96,18 @@ static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int
/* Check that the supplied MD5 hash matches that stored for the supplied filename */
static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn)
{
if (memcmp(file.hash, digest, sizeof(file.hash)) == 0) return true;
if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename);
return false;
uint i;
/* Loop through each byte of the file MD5 and the stored MD5... */
for (i = 0; i < 16; i++) if (file.hash[i] != digest[i]) break;
/* If all bytes of the MD5's match (i.e. the MD5's match)... */
if (i == 16) {
return true;
} else {
if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename);
return false;
};
}
/* Calculate and check the MD5 hash of the supplied filename.
@@ -111,30 +118,19 @@ static bool FileMD5(const MD5File file, bool warn)
char buf[MAX_PATH];
// open file
snprintf(buf, lengthof(buf), "%s%s", _paths.data_dir, file.filename);
sprintf(buf, "%s%s", _path.data_dir, file.filename);
f = fopen(buf, "rb");
#if !defined(WIN32)
if (f == NULL) {
strtolower(buf + strlen(_paths.data_dir) - 1);
char *s;
// make lower case and check again
for (s = buf + strlen(_path.data_dir) - 1; *s != '\0'; s++)
*s = tolower(*s);
f = fopen(buf, "rb");
}
#endif
#if defined SECOND_DATA_DIR
/* If we failed to find the file in the first data directory, we will try the other one */
if (f == NULL) {
snprintf(buf, lengthof(buf), "%s%s", _paths.second_data_dir, file.filename);
f = fopen(buf, "rb");
if (f == NULL) {
strtolower(buf + strlen(_paths.second_data_dir) - 1);
f = fopen(buf, "rb");
}
}
#endif
if (f != NULL) {
md5_state_t filemd5state;
md5_byte_t buffer[1024];
@@ -145,7 +141,7 @@ static bool FileMD5(const MD5File file, bool warn)
while ((len = fread(buffer, 1, sizeof(buffer), f)) != 0)
md5_append(&filemd5state, buffer, len);
if (ferror(f) && warn) ShowInfoF("Error Reading from %s \n", buf);
if (ferror(f) && warn) fprintf(stderr, "Error Reading from %s \n", buf);
fclose(f);
md5_finish(&filemd5state, digest);
@@ -174,13 +170,7 @@ void CheckExternalFiles(void)
for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++;
if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false))
ShowInfo("Your 'sample.cat' file is corrupted or missing!");
for (i = 0; i < lengthof(files_openttd); i++) {
if (!FileMD5(files_openttd[i], false)) {
ShowInfoF("Your '%s' file is corrupted or missing!", files_openttd[i].filename);
}
}
fprintf(stderr, "Your sample.cat file is corrupted or missing!\n");
/*
* forced DOS palette via command line -> leave it that way
@@ -286,6 +276,7 @@ static const SpriteID trg1idx[] = {
* the old sprite-count offset from SPR_OPENTTD_BASE. With this there is no
* correspondence of any kind with the ID's in the grf file, but results in
* a maximum use of sprite slots. */
#define OPENTTD_SPRITES_COUNT 95
static const SpriteID _openttd_grf_indexes[] = {
SPR_IMG_AUTORAIL, SPR_CURSOR_WAYPOINT, // icons etc
134, 134, // euro symbol medium size
@@ -296,7 +287,7 @@ static const SpriteID _openttd_grf_indexes[] = {
616, 616, // nordic char: <20>
666, 666, // nordic char: <20>
634, 634, // nordic char: <20>
SPR_PIN_UP, SPR_CURSOR_CLONE_TRAIN, // more icons
SPR_PIN_UP, SPR_CURSOR_CLONE, // more icons
382, 383, // <20> <20> tiny
158, 159, // <20> <20> medium
606, 607, // <20> <20> large
@@ -329,13 +320,10 @@ static const SpriteID _openttd_grf_indexes[] = {
594, 597, // <20> <20> <20> <20> large
633, 633, // <20> large
665, 665, // <20> large
SPR_SELL_TRAIN, SPR_SHARED_ORDERS_ICON,
377, 377, // <20> small
153, 153, // <20> medium
601, 601, // <20> large
END
};
static byte _sprite_page_to_load = 0xFF;
static void LoadSpriteTables(void)
{
@@ -353,18 +341,14 @@ static void LoadSpriteTables(void)
load_index += LoadGrfFile(files->basic[i].filename, load_index, i);
}
/* Load additional sprites for climates other than temperate */
if (_opt.landscape != LT_NORMAL) {
if (_sprite_page_to_load != 0) {
LoadGrfIndexed(
files->landscape[_opt.landscape - 1].filename,
_landscape_spriteindexes[_opt.landscape - 1],
files->landscape[_sprite_page_to_load - 1].filename,
_landscape_spriteindexes[_sprite_page_to_load - 1],
i++
);
}
assert(load_index == SPR_SIGNALS_BASE);
load_index += LoadGrfFile("nsignalsw.grf", load_index, i++);
assert(load_index == SPR_CANALS_BASE);
load_index += LoadGrfFile("canalsw.grf", load_index, i++);
@@ -374,31 +358,25 @@ static void LoadSpriteTables(void)
load_index = SPR_AUTORAIL_BASE;
load_index += LoadGrfFile("autorail.grf", load_index, i++);
assert(load_index == SPR_ELRAIL_BASE);
load_index += LoadGrfFile("elrailsw.grf", load_index, i++);
assert(load_index == SPR_2CCMAP_BASE);
load_index += LoadGrfFile("2ccmap.grf", load_index, i++);
assert(load_index == SPR_OPENTTD_BASE);
LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++);
load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT;
assert(load_index == SPR_AIRPORTX_BASE);
load_index += LoadGrfFile("airports.grf", load_index, i++);
/* Initialize the unicode to sprite mapping table */
InitializeUnicodeGlyphMap();
LoadNewGRF(load_index, i);
}
void GfxLoadSprites(void)
{
DEBUG(spritecache, 1) ("Loading sprite set %d.", _opt.landscape);
// Need to reload the sprites only if the landscape changed
if (_sprite_page_to_load != _opt.landscape) {
_sprite_page_to_load = _opt.landscape;
GfxInitSpriteMem();
LoadSpriteTables();
GfxInitPalettes();
// Sprite cache
DEBUG(spritecache, 1) ("Loading sprite set %d.", _sprite_page_to_load);
GfxInitSpriteMem();
LoadSpriteTables();
GfxInitPalettes();
}
}

File diff suppressed because it is too large Load Diff

46
gui.h
View File

@@ -5,7 +5,6 @@
#include "station.h"
#include "window.h"
#include "string.h"
/* main_gui.c */
void SetupColorsAndInitialWindow(void);
@@ -17,10 +16,9 @@ void CcTerraform(bool success, TileIndex tile, uint32 p1, uint32 p2);
void ShowGameOptions(void);
void ShowGameDifficulty(void);
void ShowPatchesSelection(void);
void DrawArrowButtons(int x, int y, int ctab, byte state, bool clickable_left, bool clickable_right);
void ShowNewgrf(void);
/* graph_gui.c */
extern const byte _cargo_colours[NUM_CARGO];
void ShowOperatingProfitGraph(void);
void ShowIncomeGraph(void);
void ShowDeliveredCargoGraph(void);
@@ -35,37 +33,38 @@ void ShowLastNewsMessage(void);
void ShowMessageOptions(void);
void ShowMessageHistory(void);
/* rail_gui.c */
/* traintoolb_gui.c */
void ShowBuildRailToolbar(RailType railtype, int button);
void PlaceProc_BuyLand(TileIndex tile);
void ReinitGuiAfterToggleElrail(bool disable);
/* train_gui.c */
void ShowPlayerTrains(PlayerID player, StationID station);
void ShowTrainViewWindow(const Vehicle *v);
void ShowOrdersWindow(const Vehicle *v);
void ShowOrdersWindow(const Vehicle* v);
void ShowRoadVehViewWindow(const Vehicle* v);
/* road_gui.c */
void ShowBuildRoadToolbar(void);
void ShowBuildRoadScenToolbar(void);
void ShowRoadVehViewWindow(const Vehicle *v);
void ShowPlayerRoadVehicles(PlayerID player, StationID station);
/* dock_gui.c */
void ShowBuildDocksToolbar(void);
void ShowShipViewWindow(const Vehicle *v);
void ShowPlayerShips(PlayerID player, StationID station);
void ShowShipViewWindow(const Vehicle* v);
/* aircraft_gui.c */
void ShowBuildAirToolbar(void);
void ShowPlayerAircraft(PlayerID player, StationID station);
/* terraform_gui.c */
void ShowTerraformToolbar(void);
/* tgp_gui.c */
void ShowGenerateLandscape(void);
void ShowHeightmapLoad(void);
void PlaceProc_DemolishArea(TileIndex tile);
void PlaceProc_LevelLand(TileIndex tile);
bool GUIPlaceProcDragXY(const WindowEvent *e);
bool GUIPlaceProcDragXY(const WindowEvent *we);
enum { // max 32 - 4 = 28 types
GUI_PlaceProc_DemolishArea = 0 << 4,
@@ -93,7 +92,7 @@ void ShowEstimatedCostOrIncome(int32 cost, int x, int y);
void ShowErrorMessage(StringID msg_1, StringID msg_2, int x, int y);
void DrawStationCoverageAreaText(int sx, int sy, uint mask,int rad);
void CheckRedrawStationCoverage(const Window *w);
void CheckRedrawStationCoverage(const Window* w);
void ShowSmallMap(void);
void ShowExtraViewPortWindow(void);
@@ -102,6 +101,7 @@ void SetVScroll2Count(Window *w, int num);
void SetHScrollCount(Window *w, int num);
void ShowCheatWindow(void);
void AskForNewGameToStart(void);
void DrawEditBox(Window *w, querystr_d *string, int wid);
void HandleEditBox(Window *w, querystr_d *string, int wid);
@@ -110,24 +110,32 @@ bool HandleCaret(Textbuf *tb);
void DeleteTextBufferAll(Textbuf *tb);
bool DeleteTextBufferChar(Textbuf *tb, int delmode);
bool InsertTextBufferChar(Textbuf *tb, uint32 key);
bool InsertTextBufferChar(Textbuf *tb, byte key);
bool InsertTextBufferClipboard(Textbuf *tb);
bool MoveTextBufferPos(Textbuf *tb, int navmode);
void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth);
void UpdateTextBufferSize(Textbuf *tb);
void BuildFileList(void);
void SetFiosType(const byte fiostype);
/* FIOS_TYPE_FILE, FIOS_TYPE_OLDFILE etc. different colours */
/* FIOS_TYPE_FILE, FIOS_TYPE_OLDFILE etc. different colours */
extern const byte _fios_colors[];
/* network gui */
void ShowNetworkGameWindow(void);
/* bridge_gui.c */
void ShowBuildBridgeWindow(uint start, uint end, byte type);
enum {
ZOOM_IN = 0,
ZOOM_OUT = 1,
ZOOM_NONE = 2, // hack, used to update the button status
};
bool DoZoomInOutWindow(int how, Window * w);
void ShowBuildIndustryWindow(void);
void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, WindowClass window_class, WindowNumber window_number, CharSetFilter afilter);
void ShowQuery(StringID caption, StringID message, void (*ok_cancel_callback)(bool ok_clicked), WindowClass window_class, WindowNumber window_number);
void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, WindowClass window_class, WindowNumber window_number);
void ShowMusicWindow(void);
/* main_gui.c */

50
hal.h
View File

@@ -42,7 +42,55 @@ enum DriverType {
MUSIC_DRIVER = 2,
};
void GameLoop(void);
extern void GameLoop(void);
// Deals with finding savegames
typedef struct {
byte type;
uint64 mtime;
char title[64];
char name[256-12-64];
} FiosItem;
enum {
FIOS_TYPE_DRIVE = 0,
FIOS_TYPE_PARENT = 1,
FIOS_TYPE_DIR = 2,
FIOS_TYPE_FILE = 3,
FIOS_TYPE_OLDFILE = 4,
FIOS_TYPE_SCENARIO = 5,
FIOS_TYPE_OLD_SCENARIO = 6,
FIOS_TYPE_DIRECT = 7,
};
// Variables to display file lists
FiosItem *_fios_list;
int _fios_num;
int _saveload_mode;
// get the name of an oldstyle savegame
void GetOldSaveGameName(char *title, const char *file);
// get the name of an oldstyle scenario
void GetOldScenarioGameName(char *title, const char *file);
// Get a list of savegames
FiosItem *FiosGetSavegameList(int *num, int mode);
// Get a list of scenarios
FiosItem *FiosGetScenarioList(int *num, int mode);
// Free the list of savegames
void FiosFreeSavegameList(void);
// Browse to. Returns a filename w/path if we reached a file.
char *FiosBrowseTo(const FiosItem *item);
// Return path, free space and stringID
StringID FiosGetDescText(const char **path, uint32 *tot);
// Delete a name
bool FiosDelete(const char *name);
// Make a filename from a name
void FiosMakeSavegameName(char *buf, const char *name, size_t size);
int CDECL compare_FiosItems(const void *a, const void *b);
void CreateConsole(void);

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More