[atanks] 05/13: Import Upstream version 6.4~dfsg

Markus Koschany apo at moszumanska.debian.org
Sat Nov 26 23:05:36 UTC 2016


This is an automated email from the git hooks/post-receive script.

apo pushed a commit to branch master
in repository atanks.

commit a29b77907e90ba934ac1c6a0e54a7856497ab1a9
Author: Markus Koschany <apo at debian.org>
Date:   Sat Nov 26 23:42:43 2016 +0100

    Import Upstream version 6.4~dfsg
---
 Changelog                            |  347 +-
 Makefile                             |  413 +-
 Makefile.bsd                         |  298 ++
 README                               |   17 +-
 TODO                                 |   19 +-
 allegro.cfg                          |    2 +
 allegro.supp                         |  238 ++
 cb/atanks.cbp                        |  438 ++
 cb/atanks.workspace                  |    6 +
 credits.txt                          |    2 +
 dep/.keep_dir                        |    0
 dep/aicore.d                         |    5 +
 dep/atanks.d                         |    8 +
 dep/beam.d                           |    5 +
 dep/button.d                         |    3 +
 dep/client.d                         |    6 +
 dep/clock.d                          |    1 +
 dep/debris_pool.d                    |    3 +
 dep/debug.d                          |    1 +
 dep/decor.d                          |    4 +
 dep/environment.d                    |    5 +
 dep/explosion.d                      |    5 +
 dep/files.d                          |    3 +
 dep/floattext.d                      |    3 +
 dep/gameloop.d                       |    7 +
 dep/gfxData.d                        |    3 +
 dep/globaldata.d                     |    5 +
 dep/globaltypes.d                    |    1 +
 dep/land.d                           |    4 +
 dep/main.d                           |    3 +
 dep/menu.d                           |    6 +
 dep/missile.d                        |    6 +
 dep/network.d                        |    3 +
 dep/optionitembase.d                 |    6 +
 dep/optionitemcolour.d               |    4 +
 dep/optionitemmenu.d                 |    5 +
 dep/optionitemplayer.d               |    5 +
 dep/optionscreens.d                  |    6 +
 dep/optiontypes.d                    |    1 +
 dep/perlin.d                         |    3 +
 dep/physobj.d                        |    3 +
 dep/player.d                         |    7 +
 dep/player_types.d                   |    3 +
 dep/satellite.d                      |    4 +
 dep/shop.d                           |    4 +
 dep/sky.d                            |    4 +
 dep/sound.d                          |    3 +
 dep/tank.d                           |    5 +
 dep/teleport.d                       |    5 +
 dep/text.d                           |    3 +
 dep/update.d                         |    3 +
 dep/virtobj.d                        |    3 +
 do_helgrind.sh                       |    5 +
 do_memcheck.sh                       |    7 +
 gdb_memcheck.sh                      |    7 +
 obj/.keep_dir                        |    0
 sound/0.wav                          |  Bin 12461 -> 0 bytes
 sound/00.wav                         |  Bin 0 -> 25118 bytes
 sound/01.wav                         |  Bin 0 -> 72094 bytes
 sound/02.wav                         |  Bin 0 -> 67612 bytes
 sound/03.wav                         |  Bin 0 -> 82466 bytes
 sound/04.wav                         |  Bin 0 -> 107806 bytes
 sound/05.wav                         |  Bin 0 -> 30746 bytes
 sound/06.wav                         |  Bin 0 -> 120580 bytes
 sound/07.wav                         |  Bin 0 -> 170304 bytes
 sound/1.wav                          |  Bin 35948 -> 0 bytes
 sound/10.wav                         |  Bin 966 -> 173796 bytes
 sound/11.wav                         |  Bin 30139 -> 177384 bytes
 sound/12.wav                         |  Bin 81202 -> 178404 bytes
 sound/13.wav                         |  Bin 0 -> 265464 bytes
 sound/14.wav                         |  Bin 0 -> 505014 bytes
 sound/15.wav                         |  Bin 0 -> 69952 bytes
 sound/16.wav                         |  Bin 0 -> 138894 bytes
 sound/17.wav                         |  Bin 0 -> 145640 bytes
 sound/18.wav                         |  Bin 0 -> 256332 bytes
 sound/19.wav                         |  Bin 0 -> 288116 bytes
 sound/2.wav                          |  Bin 33708 -> 0 bytes
 sound/20.wav                         |  Bin 0 -> 177500 bytes
 sound/21.wav                         |  Bin 0 -> 45320 bytes
 sound/22.wav                         |  Bin 0 -> 63704 bytes
 sound/3.wav                          |  Bin 41126 -> 0 bytes
 sound/30.wav                         |  Bin 0 -> 162548 bytes
 sound/31.wav                         |  Bin 0 -> 327384 bytes
 sound/{10.wav => 32.wav}             |  Bin
 sound/4.wav                          |  Bin 43436 -> 0 bytes
 sound/{8.wav => 40.wav}              |  Bin
 sound/5.wav                          |  Bin 44332 -> 0 bytes
 sound/6.wav                          |  Bin 44588 -> 0 bytes
 sound/7.wav                          |  Bin 66348 -> 0 bytes
 sound/9.wav                          |  Bin 34904 -> 0 bytes
 src/Makefile                         |  149 -
 src/Makefile.bsd                     |  151 -
 src/Makefile.windows                 |  157 -
 src/aicore.cpp                       | 5584 +++++++++++++++++++++++++
 src/aicore.h                         |  950 +++++
 src/allegro.h                        |   82 -
 src/atanks.cpp                       | 7461 +++++++---------------------------
 src/atanks.rc                        |  119 +-
 src/beam.cpp                         |  679 +++-
 src/beam.h                           |  108 +-
 src/button.cpp                       |  158 +-
 src/button.h                         |   73 +-
 src/client.cpp                       |  640 ++-
 src/client.h                         |   16 +-
 src/clock.cpp                        |   66 +
 src/clock.h                          |   25 +
 src/debris_pool.cpp                  |  238 ++
 src/debris_pool.h                    |   73 +
 src/debug.cpp                        |   64 +
 src/debug.h                          |  168 +
 src/decor.cpp                        |  441 ++
 src/decor.h                          |  215 +-
 src/environment.cpp                  | 2491 +++++++-----
 src/environment.h                    |  396 +-
 src/explosion.cpp                    | 1313 ++++--
 src/explosion.h                      |  116 +-
 src/extern/dirent.c                  |  152 +
 src/extern/dirent.h                  |   50 +
 src/externs.h                        |   67 +-
 src/fade.cpp                         |  235 --
 src/files.cpp                        | 1731 ++++----
 src/files.h                          |   91 +-
 src/floattext.cpp                    |  567 ++-
 src/floattext.h                      |  131 +-
 src/gameloop.cpp                     | 2716 ++++++++++---
 src/gameloop.h                       |   65 +-
 src/gfxData.cpp                      |  295 ++
 src/gfxData.h                        |   77 +
 src/globaldata.cpp                   | 2363 +++++------
 src/globaldata.h                     |  402 +-
 src/globals.h                        |  317 +-
 src/globaltypes.cpp                  |   68 +
 src/globaltypes.h                    |  337 ++
 src/land.cpp                         |  423 +-
 src/land.h                           |  191 +-
 src/main.cpp                         |  156 +
 src/main.h                           |  855 ++--
 src/menu.cpp                         | 1214 ++++++
 src/menu.h                           |  463 ++-
 src/menucontent.h                    |  852 ----
 src/missile.cpp                      | 1823 +++++----
 src/missile.h                        |  116 +-
 src/network.cpp                      |   71 +-
 src/network.h                        |   33 +-
 src/optioncontent.h                  | 1326 ++++++
 src/optionitem.h                     |  567 +++
 src/optionitembase.cpp               |  768 ++++
 src/optionitembase.h                 |  207 +
 src/optionitemcolour.cpp             |  203 +
 src/optionitemcolour.h               |   94 +
 src/optionitemmenu.cpp               |  123 +
 src/optionitemmenu.h                 |   86 +
 src/optionitemplayer.cpp             |  231 ++
 src/optionitemplayer.h               |   92 +
 src/optionscreens.cpp                | 1038 +++++
 src/{imagedefs.h => optionscreens.h} |   28 +-
 src/optiontypes.cpp                  |  187 +
 src/optiontypes.h                    |  128 +
 src/perlin.cpp                       |    6 +-
 src/physobj.cpp                      |  797 +++-
 src/physobj.h                        |  109 +-
 src/player.cpp                       | 7291 +++++++++++++--------------------
 src/player.h                         |  388 +-
 src/player_types.cpp                 |  150 +
 src/player_types.h                   |  130 +
 src/resource.h                       | 1569 +++++++
 src/satellite.cpp                    |  100 +-
 src/satellite.h                      |   54 +-
 src/shop.cpp                         | 1002 +++++
 src/shop.h                           |   12 +
 src/sky.cpp                          |  560 ++-
 src/sky.h                            |  190 +-
 src/sound.cpp                        |  174 +
 src/{imagedefs.h => sound.h}         |   32 +-
 src/tank.cpp                         | 2891 +++++++------
 src/tank.h                           |  179 +-
 src/team.cpp                         |   35 -
 src/team.h                           |   26 -
 src/teleport.cpp                     |  340 +-
 src/teleport.h                       |   78 +-
 src/text.cpp                         |  400 +-
 src/text.h                           |   79 +-
 src/update.cpp                       |  209 +-
 src/update.h                         |   68 +-
 src/virtobj.cpp                      |  322 +-
 src/virtobj.h                        |  191 +-
 src/winclock.h                       |  105 +
 tankgun/2.bmp                        |  Bin 3126 -> 3126 bytes
 tankgun/4.bmp                        |  Bin 4150 -> 3126 bytes
 tankgun/5.bmp                        |  Bin 3126 -> 3126 bytes
 tankgun/7.bmp                        |  Bin 4150 -> 3126 bytes
 text/panic.pt_BR.txt                 |    8 +
 text/panic.txt                       |    8 +
 text/panic_ES.txt                    |    8 +
 text/panic_de.txt                    |    8 +
 text/panic_fr.txt                    |    8 +
 text/panic_it.txt                    |    8 +
 text/panic_ru.txt                    |    8 +
 text/panic_sk.txt                    |    8 +
 text/weapons.pt_BR.txt               |   85 -
 text/weapons.txt                     |  138 +-
 text/weapons_ES.txt                  |   85 -
 text/weapons_de.txt                  |   85 -
 text/weapons_fr.txt                  |   85 -
 text/weapons_it.txt                  |   85 -
 text/weapons_ru.txt                  |   85 -
 text/weapons_sk.txt                  |   85 -
 title/.directory                     |    3 -
 unicode.dat                          |  Bin 969257 -> 5604 bytes
 vs12/README_allegro.txt              |   14 +
 vs12/atanks.sln                      |   28 +
 vs12/atanks.vcxproj                  |  285 ++
 vs12/atanks.vcxproj.filters          |  293 ++
 213 files changed, 40093 insertions(+), 23652 deletions(-)

diff --git a/Changelog b/Changelog
index ecc9b51..ec05dc1 100644
--- a/Changelog
+++ b/Changelog
@@ -1,5 +1,74 @@
 NOTE: From now on, new changes appear at the top of this file.
 
+============ Atanks-6.3 released ===================
+
+	Sven has done a huge amount of code changes for this release. The
+	key highlights are as follows:
+	- Improved AI.
+	- Smoother/faster buying screen navigation.
+	- Huge re-work of the Options screen and other menus to
+	  avoid crashes and random symbols appearing.
+	- Improved demo mode.
+	- The AI does a better job of buying items/weapons.
+	- Updated Makefiles to reduce the number of separate Makefiles required.
+	- Atanks should now build natively in Visual Studio on Windows.
+	- Improved Unicode support.
+	- Various code clean-up.
+	- Added dirt debris.
+	- Improved revenge mode for AI.
+	- Menus should now respond faster.
+	- Network code clean-up.
+	- Added more in-code documentation.
+
+============ Atanks-6.2 released ===================
+
+	- Large code clean-up by Bruno Victal
+	- More style/code clean-up by Bruno Victal
+	    Indentation
+	    Removing unnecessary braces
+	    Removing unnecessary parentheses
+	    Removing unnecessary spaces
+	    Removing many blank lines (line wasting)
+	    Expanding some ifs and switch cases
+
+	- Bruno Victal provided further code clean-up
+	    Renamed old referenced files in the perror calls (from .cc to .cpp)
+	    Removed some useless casts in printf calls [ printf((char *)"String here"); ]
+	    Deleted the macros about DOS support (they were already commented out)
+	    Fixed abs() related math functions so they do not throw compiler warnings.
+
+	- Removed extra call to Get_Team_Name() that was not required.
+
+	Daniel sent in a patch which addresses the following:
+	- fixes a bug where the address of (stack allocated) temporal arrays
+	    was stored in the menu structure causing
+	    wrong behavior, e.g. printing garbage in the Network configuration
+	    menu title.
+	- fixes const-correctness for all the menu structure
+	- moves stack-allocated read-only arrays to .rodata so no large stack
+	    memory is used.
+	- adds Daniel to the list of people (feel free to remove me if this
+
+	Bruno Victal provided two more fixes.
+	- Fix isnan() ambiguity when compiled with C++11 standards
+	- Removed OLD_GAMELOOP code and references. Stick with
+	    modern game loop.
+
+
+=========== Atanks-6.1 released ==============
+	All of this releases patches were provided by Bill Buerger.
+	- Extend the amount of time the AI will play against itself
+	  from 10 seconds to 60 seconds.
+	- Fixed "clean" command in Windows Makefile.
+	- Players now wait to start their turn until they
+	  can control their tank. No more waiting at the start
+	  of their turn.
+	- Rollers now properly explode when hitting steel wall.
+	- Scoreboard shows current income and turn order.
+	  This can be toggled with the ~ key.
+	- Fixed navigation keys on the store/buying sceen.
+
+
 =========== Atanks-6.0 released ==============
 	- Removed extra alleg42.dll file from src directory.
         - Clarified license.
@@ -101,7 +170,7 @@ Feb 23, 2012
 ============ Atanks-5.0 released ===============
 
 June 4, 2011
-    - Window's close button now works on buying screen 
+    - Window's close button now works on buying screen
       and during rounds.
 
 
@@ -109,7 +178,7 @@ June 4, 2011
 
 
 March 23, 2011
-    - Added Italian language support. 
+    - Added Italian language support.
       (provided by Roby Alice.)
 
 
@@ -450,7 +519,7 @@ September 19, 2009
     - Removed old lineseq.h file and references.
     - Cleaned up renderTextLines function to display cyrillic
       text properly.
-      
+
 
 September 14, 2009
     - Added unicode fonts to support cyrillic.
@@ -696,7 +765,7 @@ June 19, 2009
 
 June 15, 2009
     - Fixed abs() calls in player.cpp and tank.cpp
-    - Replaced old Teleport animation with Yama's. 
+    - Replaced old Teleport animation with Yama's.
 
 June 4, 2009
     - Added war quotes to display at the end of games. (files.cpp, files.h,
@@ -765,179 +834,179 @@ March 19, 2009
 
 February 21, 2009
     -- Finally "-Wno-write-strings" is no longer needed to compile the sources
-    -- Rewrote the environment method to calculate average background colors. It is 
-    now inline and somewhat optimized, making shadowed + fading text more 
+    -- Rewrote the environment method to calculate average background colors. It is
+    now inline and somewhat optimized, making shadowed + fading text more
     performant.
-    -- Rewrote File handling, apart from three tiny points atanks is now fully ISO 
+    -- Rewrote File handling, apart from three tiny points atanks is now fully ISO
     C++ compliant.
     -- Made rollers a bit faster dropping
-    -- No more roller-breaking of shields by hammering them above the enemy onto 
+    -- No more roller-breaking of shields by hammering them above the enemy onto
     the steel/wrap ceiling! They explode now.
-    -- Two tiny patches make bots targetting a bit faster and destroy (hopefully) 
+    -- Two tiny patches make bots targetting a bit faster and destroy (hopefully)
     any possibility for the system to go infinite
     -- prepared all neccessary files to be translated. But please note:
     -- English: Is always there
-    -- Portuguese: Is borked. But maybe it's only my utf-8 system showing the 
-    wrong characters. I looked into the atanks-2.9 files and it is broken there as 
+    -- Portuguese: Is borked. But maybe it's only my utf-8 system showing the
+    wrong characters. I looked into the atanks-2.9 files and it is broken there as
     well. If its just my system, don't mind. ;)
-    -- French: Has some tiny things that has to be translated. Retaliation is 
-    still english, and I am not sure how to translate "Shadowed Text" and "Fading 
+    -- French: Has some tiny things that has to be translated. Retaliation is
+    still english, and I am not sure how to translate "Shadowed Text" and "Fading
     Text", so some french natural speaker might do this?
     -- German: Only weapons_de.txt needs to be translated, I'll do it next year.
-    -- Slovak: Only the gloating texts *are* translated so the rest is missing 
-    completely, but I have prepared the files and menucontent.h for a slovak 
+    -- Slovak: Only the gloating texts *are* translated so the rest is missing
+    completely, but I have prepared the files and menucontent.h for a slovak
     natural speaker to be translated.
-    -- Added weapons_*language*.txt files so we can (finally) translate the weapon 
+    -- Added weapons_*language*.txt files so we can (finally) translate the weapon
     texts as well.
     -- Fixed a bug that could cause roller tracking to go infinite.
     -- Balanced fading texts some more
     -- Corrected some text messages
     -- added new roller code. Rollers now really roll!
-    -- fixed a bug that made tracking report wrong hit points when tracking 
+    -- fixed a bug that made tracking report wrong hit points when tracking
     rollers and burrowers
-    -- Added average background color calculation and fixed fading text offset. It 
+    -- Added average background color calculation and fixed fading text offset. It
     should look far better now.
     -- Balanced shadowed text color calculation
-    -- added a new retaliation text. When bots aim at opponents they have a grudge 
+    -- added a new retaliation text. When bots aim at opponents they have a grudge
     against, they might tell you now.
-    For this to work you'll have to copy retaliation*.txt into 
-    /usr/share/games/atanks/ (or wherever you have your atanks data files) 
+    For this to work you'll have to copy retaliation*.txt into
+    /usr/share/games/atanks/ (or wherever you have your atanks data files)
     directory.
-    -- removed waiting for explosions to finish when tanks blow up after being hit 
+    -- removed waiting for explosions to finish when tanks blow up after being hit
     by violent death missiles.
-    I was longing to change that for ages, because I grew rather sick of tanks 
-    being suspended in mid-air for ages while all those violent death missile fly 
-    around. Now, they blow up immediately. However, when a normal missile is shot 
+    I was longing to change that for ages, because I grew rather sick of tanks
+    being suspended in mid-air for ages while all those violent death missile fly
+    around. Now, they blow up immediately. However, when a normal missile is shot
     and destroys a tank, it still waits for the missile's explosion to finish.
-    -- changed tank falling behavior: They now can fall a short distance (2-5 
+    -- changed tank falling behavior: They now can fall a short distance (2-5
     pixels) without getting damage and without the parachutes to open.
-    I was longing for that one, too. The reason is, that it is madness to waste 
-    30+ parachutes and/or getting alot of damage while moving (!!) a tank down a 
+    I was longing for that one, too. The reason is, that it is madness to waste
+    30+ parachutes and/or getting alot of damage while moving (!!) a tank down a
     flat slope.
-    -- Fixed a bug with the tracing of spreads which resulted in bots hitting self 
+    -- Fixed a bug with the tracing of spreads which resulted in bots hitting self
     but not the opponent.
-    -- added a security check when calculating offsets to napalm. Bots should not 
+    -- added a security check when calculating offsets to napalm. Bots should not
     drop napalm onto themselves any more when trying to hit a neighbor.
-    -- added shadows to the text to increase readability. (Has to be turned on in 
+    -- added shadows to the text to increase readability. (Has to be turned on in
     the graphics menu, for compatibilities sake this option defaults to "off")
-    -- added fading to the text, to ... well... to have a bit of eye candy. ;) 
-    (Has to be turned on in the graphics menu, for compatibilities sake this 
+    -- added fading to the text, to ... well... to have a bit of eye candy. ;)
+    (Has to be turned on in the graphics menu, for compatibilities sake this
     option defaults to "off")
     -- cleaned up aiming calculation
     -- cleaned up debugging messages for "DEBUG_AIM" and added some more info.
-    -- Fixed a bug that could cause riot shots and blasts to start 10-20 pixels 
+    -- Fixed a bug that could cause riot shots and blasts to start 10-20 pixels
     away from the tank cannon.
-    -- Fixed a bug that caused the the aiming system to fail in a very special 
-    (and rare, but still existing) situation, resulting in bots shooting into the 
+    -- Fixed a bug that caused the the aiming system to fail in a very special
+    (and rare, but still existing) situation, resulting in bots shooting into the
     ceiling.
     -- Added a cleanup for all objects. No more debris on the screen!
-    -- As the new aiming system doesn't need it any more, the wall type and boxed 
-    mode aren't changed any more once the last human player dies and skipping is 
+    -- As the new aiming system doesn't need it any more, the wall type and boxed
+    mode aren't changed any more once the last human player dies and skipping is
     turned on.
     -- added boxed mode (Finally!)
-    -- riot blasts fixed, they no longer shoot downwards when too less power is 
+    -- riot blasts fixed, they no longer shoot downwards when too less power is
     chosen
     -- chain missiles now blast through dirt. (vertical spreads, too)
-    -- Walltypes on non-human rounds with skip-AI on will now all change to 
-    Wrapped Walls & non-Boxed mode, but not before the current destructions 
+    -- Walltypes on non-human rounds with skip-AI on will now all change to
+    Wrapped Walls & non-Boxed mode, but not before the current destructions
     (violent deaths, falling tanks) are finished. (happy, sylikc? ;))
-    -- a completely new aiming system has been written to make the boxed mode 
+    -- a completely new aiming system has been written to make the boxed mode
     possible
     -- added tweaks to reduce "debris" left on screen
     -- added tweaks to reduce "choking" of the display when too much is going on
-    -- removed HLR_DEBUG, because in over 1000 rounds the speed-up-system never 
+    -- removed HLR_DEBUG, because in over 1000 rounds the speed-up-system never
     failed
     -- There are now three different defines for debugging:
         -- DEBUG
-        Will show everything concerning inventories, shopping system, item 
+        Will show everything concerning inventories, shopping system, item
         selection and target selection of the bots on the console.
         -- DEBUG_AIM
         Will show all relevant numbers of the new aiming system on the console
         -- DEBUG_AIM_SHOW
-        Will draw dots and circles on the screen to make the "thinking" of the 
+        Will draw dots and circles on the screen to make the "thinking" of the
         aiming process visible.
         (Warning: The game is no longer playable in this mode)
-    -- balanced computer player values, according to the new aiming system. 
-    Generally speaking, the "useless" bot won't hit a barn with a pumpgun, while 
+    -- balanced computer player values, according to the new aiming system.
+    Generally speaking, the "useless" bot won't hit a barn with a pumpgun, while
     the "deadly" bot is very precise.
-    -- If no target can be reached by normal means, bots now might teleport (or 
-    swap) out of their corner, or try to get rid of the obstacle by using riot 
+    -- If no target can be reached by normal means, bots now might teleport (or
+    swap) out of their corner, or try to get rid of the obstacle by using riot
     blasts
-    -- While aiming bots now try to avoid hitting themselves or team mebers (when 
-    non-neutral) and are somewhat carefull not to hit themselves or team members 
-    with the blast damage their weapon produces. If a target can't be hit 
+    -- While aiming bots now try to avoid hitting themselves or team mebers (when
+    non-neutral) and are somewhat carefull not to hit themselves or team members
+    with the blast damage their weapon produces. If a target can't be hit
     otherwise, there have sacrefices to be made, though...
-    -- target selection now uses the new aiming system to value targets lower 
+    -- target selection now uses the new aiming system to value targets lower
     which are hard (or impossible) to hit
-    -- item selection now uses the new aiming system to value weapons lower with 
+    -- item selection now uses the new aiming system to value weapons lower with
     which the chosen target can't be hit properly
-    -- The target system now calculates burrowers and rollers. (Which leads to 
+    -- The target system now calculates burrowers and rollers. (Which leads to
     some highly entertaining results! :))
-    -- The more intelligent a bot is, the more "bounces" of walls or "wraps" it 
+    -- The more intelligent a bot is, the more "bounces" of walls or "wraps" it
     can calculate.
-    -- The more intelligent a bot is, the more spread it can calculate. So don't 
-    wonder if a useless or guesser bot fires a super spread, happily hitting 
+    -- The more intelligent a bot is, the more spread it can calculate. So don't
+    wonder if a useless or guesser bot fires a super spread, happily hitting
     itself. It simply couldn't see that coming.
     -- Velocity check rewritten. It now a) works and b) fixes the "Repulsor-
     Shield-Bug" :D Yes, I am very happy about that!
     It is handled in three different ways:
-        -- No Spring Wall: The limit is set by maximum power, influenced by 
+        -- No Spring Wall: The limit is set by maximum power, influenced by
         the mass of the weapon.
-        -- Spring Wall and Not Boxed: The Limit is doubled, or shooting at the 
+        -- Spring Wall and Not Boxed: The Limit is doubled, or shooting at the
         wall would destroy your missile.
-        -- Spring Wall in a box: The limit is four times the normal limit, or 
+        -- Spring Wall in a box: The limit is four times the normal limit, or
         it won't be any fun!
         I had very entertaining results with that! :)
-    -- hit 2500! This means I have now tested over 2500 rounds with the new 
+    -- hit 2500! This means I have now tested over 2500 rounds with the new
     shopping- and target-selection systems since the last changes and/or bugfixes!
     -- The Boost Value calculation is changed to be more dynamic to future changes
     -- added a private member to count how many boost items are bought
-    -- changed boost buying: inserted a per-round-limit aka iBoostItemsBought with 
+    -- changed boost buying: inserted a per-round-limit aka iBoostItemsBought with
     the help of the above count
     -- language menu fixed
     -- changed interest to be dynamically gained
-    -- Added a new define, HLR_DEBUG which, when set, causes the round to end 
-    after each bot having one last shot if no human players are left. (Default, 
+    -- Added a new define, HLR_DEBUG which, when set, causes the round to end
+    after each bot having one last shot if no human players are left. (Default,
     currently, is 16)
-    -- Fixed a bug that caused aTanks to freeze for seconds everytime too much is 
-    going on. (Explo, Text, Smoke, etc.) On some occasions there still can be a 
-    "hang" for a split second or so. But that could have been my computer, the 
+    -- Fixed a bug that caused aTanks to freeze for seconds everytime too much is
+    going on. (Explo, Text, Smoke, etc.) On some occasions there still can be a
+    "hang" for a split second or so. But that could have been my computer, the
     issue needs to be tested and watched.
-    -- Started to add boxed mode (not implemented yet! But the menu item is there 
+    -- Started to add boxed mode (not implemented yet! But the menu item is there
     and the Wall-Display is changed)
     -- Fixed a bug causing accelerated AI to crash
     -- Included sylikc's Anti-Crash-Fix
-    -- When bots have enough money, they do not spend everything. Jedi keep 50%, 
+    -- When bots have enough money, they do not spend everything. Jedi keep 50%,
     Neutrals 25% and sith 5% (the squanderers! ;) *hrhr*) for "bad times"
     -- Repulsor Shield handling changed:
-        changed ydist to be always negative, so missiles are *always* handled 
-        like they came from above! It remains to be tested whether this 
-        balances the repulsor shields, or renders them unbeatable (which could 
+        changed ydist to be always negative, so missiles are *always* handled
+        like they came from above! It remains to be tested whether this
+        balances the repulsor shields, or renders them unbeatable (which could
         happen...)
-        Note: In over 500 rounds they perform better, but are still far away 
+        Note: In over 500 rounds they perform better, but are still far away
         from being "unbeatable"
-    -- When determining pre-Buy items bots enforce buying of boost-Items if they 
+    -- When determining pre-Buy items bots enforce buying of boost-Items if they
     are too far away from the peak.
     -- Limit for items raised up to 999 (like humans)
-    -- limited AI-Only Rounds. If the last human players dies the remaining bots 
-    get 10 tries each to find a winner. If they do not succeed, the round is ended 
+    -- limited AI-Only Rounds. If the last human players dies the remaining bots
+    get 10 tries each to find a winner. If they do not succeed, the round is ended
     and the most healthy tank wins. (Or the round draws if two or more are equal)
-    I added this, because there is a tiny chance (it never happened in the last 
-    weeks, but thrice only yesterday!) that the games gos infinite in AI-only 
-    turns. Situation: bots left have only small missiles and regenerate more each 
+    I added this, because there is a tiny chance (it never happened in the last
+    weeks, but thrice only yesterday!) that the games gos infinite in AI-only
+    turns. Situation: bots left have only small missiles and regenerate more each
     round than the small missile delivers.
     With this limit it won't happen any more.
-    -- When the last human dies and the wall type is changed, it is redrawn at 
+    -- When the last human dies and the wall type is changed, it is redrawn at
     once now.
     -- limited interest && team money
-    It is too easy to become super-marvel-universium-mega-hero by just waiting a 
-    couple of rounds until the bank has pumped up your account to some millions. 
-    Currently the bank refuses to pay more interest than 100k, but that hard limit 
+    It is too easy to become super-marvel-universium-mega-hero by just waiting a
+    couple of rounds until the bank has pumped up your account to some millions.
+    Currently the bank refuses to pay more interest than 100k, but that hard limit
     will be changed to a dynamic one. (see todo list)
-    As for team money, bots now refuse to spend more than 500k into the pot. 
+    As for team money, bots now refuse to spend more than 500k into the pot.
     Humans, too, of course.
     -- team win->WinRoundBonus divided instead of applied full.
-    It is just extremely unfair that the teams get much more money for winning a 
+    It is just extremely unfair that the teams get much more money for winning a
     round when it is easier for them to do so.
     -- 75% was too much, thus jedi now only pay 50% into the team account.
     -- Bots try to save up money to get better equipment
@@ -948,69 +1017,69 @@ February 21, 2009
         -- "Tools" to free themselves like Riot Blasts
         -- Shields, if enough money is there
         -- if all is set, look for dimpled/slick projectiles!
-    Armor/Amps (aka "Boost-Items"), however, are limited, so they will only buy 
-    them, if someone has a higher value in boost-items. As these items can be 
-    bought randomly as well, they won't neccessarily wait for the human player(s) 
+    Armor/Amps (aka "Boost-Items"), however, are limited, so they will only buy
+    them, if someone has a higher value in boost-items. As these items can be
+    bought randomly as well, they won't neccessarily wait for the human player(s)
     to go ahead!
-    -- Buying is no longer purely random but takes the bots weapon preferences 
+    -- Buying is no longer purely random but takes the bots weapon preferences
     into account.
     -- The amount of money a bot wants to save up is used for the target-finding-
     system as well:
-    If a bot has less money, it is more likely to target weak opponents to fetch 
-    the kill-bonus. If it has enough money, it will more likely chose the biggest 
+    If a bot has less money, it is more likely to target weak opponents to fetch
+    the kill-bonus. If it has enough money, it will more likely chose the biggest
     threat.
-    -- Money-saving is now limited, so that no bot tries to sum up millions. Right 
-    now the highest maximum, depending on type and weapon preferences, is 1M. 
+    -- Money-saving is now limited, so that no bot tries to sum up millions. Right
+    now the highest maximum, depending on type and weapon preferences, is 1M.
     (Deadly(=5) * 2 * Armageddon cost)
     -- Teams are now handled differently in alot of places:
-        -- Prior the shopping tour, every jedi adds 75%, and every sith 25% of 
-        their money to the "team-account". The team-money is reduced by a 
-        "transaction fee" (jedi: 5%, sith: 10%) and then divided onto all 
+        -- Prior the shopping tour, every jedi adds 75%, and every sith 25% of
+        their money to the "team-account". The team-money is reduced by a
+        "transaction fee" (jedi: 5%, sith: 10%) and then divided onto all
         team-members to help each other out.
-        -- While searching for a target jedi try not to hit their team mates, 
+        -- While searching for a target jedi try not to hit their team mates,
         while sith do not care so much.
-        -- While searching for a target jedi give alot less than neutrals 
-        about revenge, while sith can become furious (even against team 
+        -- While searching for a target jedi give alot less than neutrals
+        about revenge, while sith can become furious (even against team
         members!)
-        -- jedi *can* become "super-defensive" (defensive > 1.0) while sith 
-        *can* become "super-offensive (defensive < -1.0). By this jedi can be 
-        extremely cautious every now and then, while sith like to throw 
+        -- jedi *can* become "super-defensive" (defensive > 1.0) while sith
+        *can* become "super-offensive (defensive < -1.0). By this jedi can be
+        extremely cautious every now and then, while sith like to throw
         caution out of the window. :)
     -- Riot blasts/bombs are no longer chosen to be shot onto enemies!
-    -- fixed a bug with the rating of buried targets while searching for a 
+    -- fixed a bug with the rating of buried targets while searching for a
     target/chosing an apropriate weapon
     -- target selection and weapon selection now work together:
-    A bot choses a weapon and tries to find the best target for it. Then the bot 
-    decides whether another weapon would be better for that target and switches to 
+    A bot choses a weapon and tries to find the best target for it. Then the bot
+    decides whether another weapon would be better for that target and switches to
     that. But they value weapons higher they like more, now.
     -- corrected the _targetX modification for shaped charges and napalm:
-    Bots trie to aim shaped charges so that they do not directly hit, for aiming 
+    Bots trie to aim shaped charges so that they do not directly hit, for aiming
     napalm they try to take wind into account.
-    -- added blast check, to avoid using weapons which blast hit self, depending 
+    -- added blast check, to avoid using weapons which blast hit self, depending
     on intelligence and defensiveness
-    -- added laser check to see whether a laser can be fired or would hit solid 
+    -- added laser check to see whether a laser can be fired or would hit solid
     rock only
-    -- added check for buried targets and appropriate weapons (burrower, 
+    -- added check for buried targets and appropriate weapons (burrower,
     penetrator, tremor-tectonic)
-    -- added a check to get more intelligent bots to do collateral damage when 
+    -- added a check to get more intelligent bots to do collateral damage when
     possible
-    -- Added a custom Kamikaze text for the situation when bots decide to blow 
+    -- Added a custom Kamikaze text for the situation when bots decide to blow
     them selves up.
-    -- Money interest was added right before entering the shop, resulting in 
-    interest added everytime a game was reloaded (and reloaded, and reloaded...). 
-    I changed that so that interest is added after leaving the shop, fixing the 
-    "Add-money-by-reload"-Bug. Further there is no more interest for money that is 
+    -- Money interest was added right before entering the shop, resulting in
+    interest added everytime a game was reloaded (and reloaded, and reloaded...).
+    I changed that so that interest is added after leaving the shop, fixing the
+    "Add-money-by-reload"-Bug. Further there is no more interest for money that is
     just earned in the last round.
-    -- added offset if no human players left, raising the class of the bots to 
-    speed up the game without changing the difference in intelligence between bots 
+    -- added offset if no human players left, raising the class of the bots to
+    speed up the game without changing the difference in intelligence between bots
     too much.
-    -- bots perform best on rubber and wrap walls. If no human players are left, 
-    the wall type is changed if steel/spring is chosen to speed up the accelerated 
+    -- bots perform best on rubber and wrap walls. If no human players are left,
+    the wall type is changed if steel/spring is chosen to speed up the accelerated
     game.
     -- calculation of the shaped charge type weapons was improved (and corrected).
     -- Added preference generation for bots that are set to "per play" when a game
-    is loaded. The loading of the preferences is (unfortunately) disabled and I do 
-    not know why. As long as they *are* disabled, PerPlay-Config bots have to get 
+    is loaded. The loading of the preferences is (unfortunately) disabled and I do
+    not know why. As long as they *are* disabled, PerPlay-Config bots have to get
     a new config on game load, or they use the global config, rendering PerPlay-
     Config useless.
     -- For PhysObjects: reduced "virtual" height to 25 times screen height and
@@ -1043,7 +1112,7 @@ September 2, 2008
     -- Credits and Help screens now ignore extra DOS return
     characters in the text file.
     (Submitted by sylikc)
-    -- Added German language support. 
+    -- Added German language support.
     (Provided by Yama.)
     -- Changed formula for dealing damage with shaped charges.
     (Provided by Yama.)
@@ -1175,7 +1244,7 @@ March 18, 2008
     -- Removed old satellite system
     -- Created new satellite object.
     -- Satellite now fires lasers.
-    -- AI controlled tanks no longer adjust power for items. 
+    -- AI controlled tanks no longer adjust power for items.
        Saves time.
 
 
@@ -1262,7 +1331,7 @@ January 7, 2008
     -- AI players randomly select weapons rather than
        using an item until they run out.
 
-    
+
 December 28, 2007
     Jesse (jessefrgsmith at yahoo.ca)
     -- Firing a chian missile no longer fires another
@@ -1306,7 +1375,7 @@ December 9, 2007
 November 29, 2007
     Jesse (jessefrgsmith at yahoo.ca)
     -- Fixed bug where, if a tank shoots itself and falls
-    and the money penalty for self-damage drops the player's money 
+    and the money penalty for self-damage drops the player's money
     below $0, then the money is "corrected" to $1,000,000,000.
     Thanks to Andy for pointing this out.
     -- If both teams have the same number of wins, then
@@ -1334,7 +1403,7 @@ November 18, 2007
     -- Wind Strength is now saved in the settings file.
     -- Increased buffer size for options to avoid over-flow.
 
-    
+
 November 15, 2007
     Jesse (jessefrgsmith at yahoo.ca)
     -- Corrected timing bug that causes freeze on
@@ -1621,8 +1690,8 @@ March 21, 2007
     Jesse (jessefrgsmith at yahoo.ca)
     -- The player can now select to use either the custom
     Atomic Tanks mouse cursor or the default OS cursor.
-    Note: The OS cursor is probably faster. 
-    The option is located under the Graphics menu and 
+    Note: The OS cursor is probably faster.
+    The option is located under the Graphics menu and
     may require the game be restarted to take effect.
     -- The ENTER key on the number pad can now be used
     to indicate you are done entering text on Options
@@ -1654,7 +1723,7 @@ March 15, 2007
     causes all tanks to fire their weapons.
     See tank::activateSelection()
 
-    
+
 March 12, 2007
     Jesse (jessefrgsmith at yahoo.ca)
     -- The spelling of "lazer" has been changed to "laser".
@@ -1663,7 +1732,7 @@ March 12, 2007
     the other items.
     -- The Page Up and Page Down keys now cause the player's
     power to go up or down dramatically (100 points instead of 5).
-    -- Players can now choose to make their tanks 
+    -- Players can now choose to make their tanks
     appear differently. The options are Normal or Classic.
     This is changed in the Players menu.
     -- Tank style preferences are now saved in the config file.
@@ -1709,7 +1778,7 @@ Feb 24, 2007
     -- Added a device called Swapper, which will cause the
     player's tank to switch places with another tank. The
     other tank is picked at random.
-    -- Added a new wall type, Spring. The spring wall type 
+    -- Added a new wall type, Spring. The spring wall type
     causes missiles to bounce off the walls and floor
     with more speed (x1.25) than they had before.
     -- When one player kills another, the attacker gloats.
@@ -1722,7 +1791,7 @@ Feb 21, 2007
     -- Disabled window closing in Windows to prevent hangs.
     -- Added item: repair kit. A device which will slowly
     repair the player's tank each turn.
-    -- The number of rounds of play remaining 
+    -- The number of rounds of play remaining
     can be adjusted on the buying screen.
     This is performed using the "-" and "+" signs on the keyboard.
     Note, the "=" will also act like a "+" to add rounds.
@@ -1773,7 +1842,7 @@ Feb 9, 2007
     -- Changed config file to save the sound option.
     -- Made sure the command line option "--nosound"
     over-rides the sound setting in the config file.
-    -- The width and height of the Atanks window can now be 
+    -- The width and height of the Atanks window can now be
     changed from the Options menu, under Graphics. Changes
     take effect after the game is exited and re-started.
     -- Added screen width and height to the config file.
@@ -1809,7 +1878,7 @@ Jan 31, 2007
 
 Jan 27, 2007
     Jesse (jessefrgsmith at yahoo.ca)
-    -- Allow player names to be more than 10 characters. Names 
+    -- Allow player names to be more than 10 characters. Names
     should now be able to expand to 23. Changed some output
     code to allow for longer names without over-writing other
     text on the screen. Changed file format to allow longer names
@@ -1826,7 +1895,7 @@ Jan 21, 2007
     Jesse (jessefrgsmith at yahoo.ca)
     -- Floating text no longer carries over to the next round.
        Added newRound() function to FLOATTEXT object.
-    -- When the window's "X" or close button is clicked, the game 
+    -- When the window's "X" or close button is clicked, the game
        will exit immediately.
 
 =============== Atanks-2.0 Released ===========================
@@ -1888,7 +1957,7 @@ Nov 27, 2006
     -- Made player money unsigned to avoid negative funds.
     -- When player runs out of a type of ammo, game automatically
        switches to a new ammo type.
-    
+
 
 ================= Atanks-1.1.0 released ===================================
 
@@ -1921,7 +1990,7 @@ Nov 27, 2006
     -- speed up redrawing algorith
     -- new title screen
     -- new top-bar in game
-    
+
 15. 9. 2002
 
     -- fixed bug with game engine (this bug came with
@@ -1940,7 +2009,7 @@ Nov 27, 2006
     -- icons of weapons
     -- upgrade graphics
     -- release of 0.5
-    
+
 5. 01. 2003 - Tom Hudson (tom at singular.org.uk):
     -- Modified code to allow resizing of screen (currently only
     at compile time, positions now scale)
diff --git a/Makefile b/Makefile
index 4092015..82dc1c9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,40 +1,321 @@
-VERSION=6.0
-PREFIX ?= /usr/
-DESTDIR ?= 
-BINPREFIX = $(PREFIX)
+.PHONY: all install clean veryclean user winuser osxuser ubuntu \
+dist tarball zipfile source-dist i686-dist win32-dist
 
-BINDIR = ${BINPREFIX}bin/
-INSTALLDIR = ${PREFIX}share/games/atanks
+VERSION := 6.4
 
-export VERSION
-export PREFIX
-export INSTALLDIR
 
-FILENAME=atanks-${VERSION}
-INSTALL=$(PREFIX)bin/install -c
-DISTCOMMON=atanks/*.dat atanks/COPYING atanks/README atanks/TODO atanks/Changelog atanks/BUGS atanks/*.txt
-INCOMMON=COPYING README TODO Changelog *.txt unicode.dat
+DEBUG   ?= NO
+# Note: Submit as "YES" to enable debugging
+# Note: If any flag starting with -g is found in the CXXFLAGS, DEBUG is
+#       switched to YES no matter whether set otherwise or not.
 
-all:
-	FLAGS=-DLINUX $(MAKE) -C src
+# The following switches can be used to fine-tune the debugging output:
+# Note: DEBUG_AICORE can be used to enable both DEBUG_AIMING and DEBUG_EMOTION
+#       together with a single flag.
+DEBUG_AICORE  ?= NO
+DEBUG_AIMING  ?= NO
+DEBUG_EMOTION ?= NO
+DEBUG_FINANCE ?= NO
+DEBUG_OBJECTS ?= NO
+DEBUG_PHYSICS ?= NO
 
-install: 
-	mkdir -p $(DESTDIR)${BINDIR}
+# If the debug output shall be written to atanks.log, set this to YES
+DEBUG_LOG_TO_FILE ?= NO
+
+# These three are mutually exclusive. If all are set to yes,
+# address-sanitizing has priority, followed by leak, thread
+# is last.
+SANITIZE_ADDRESS ?= NO
+SANITIZE_LEAK    ?= NO
+SANITIZE_THREAD  ?= NO
+
+# The following is only used on gcc-4.9+ and only without debugging enabled.
+USE_LTO ?= NO
+
+# ------------------------------------
+# Install and target directories
+# ------------------------------------
+PREFIX     ?= /usr
+DESTDIR    ?=
+BINPREFIX  ?= $(PREFIX)
+BINDIR     ?= ${BINPREFIX}/bin
+INSTALLDIR ?= ${PREFIX}/share/games/atanks
+
+
+# ------------------------------------
+# Source files and objects
+# ------------------------------------
+SOURCES := $(wildcard src/*.cpp)
+MODULES := $(addprefix obj/,$(notdir $(SOURCES:.cpp=.o)))
+DEPENDS := $(addprefix dep/,$(notdir $(SOURCES:.cpp=.d)))
+
+
+# -------------------------------------
+# Platform to build for (Can be forced)
+# -------------------------------------
+PLATFORM ?= none
+ifeq (none,$(PLATFORM))
+  # The easiest way is to go through our make goals
+  # I know this looks weird, but the following simply means:
+  # "If the search for "win" in MAKECMDGOALS does not return an empty string"
+  ifneq (,$(findstring win,$(MAKECMDGOALS)))
+    PLATFORM := WIN32
+  else ifneq (,$(findstring osx,$(MAKECMDGOALS)))
+    PLATFORM := MACOSX
+  else
+    PLATFORM := LINUX
+  endif
+endif
+
+# If this is a user make goal, the install directory is forced to be local:
+ifneq (,$(findstring user,$(MAKECMDGOALS)))
+  INSTALLDIR := .
+endif
+
+
+# --------------------------------------------
+# Target executable and distribution file name
+# --------------------------------------------
+TARGET   := atanks
+FILENAME := $(TARGET)-$(VERSION)
+
+
+# ------------------------------------
+# Tools to use
+# ------------------------------------
+INSTALL := $(shell which install)
+RM      := $(shell which rm) -f
+CXX     ?= g++
+SED     := $(shell which sed)
+WINDRES :=
+
+ifeq (,$(findstring /,$(CXX)))
+  CXX   := $(shell which $(CXX))
+endif
+
+
+# if this is a Windows target, prefer mingw32-g++ over g++
+# Further more the WIN32 Platform needs windres.exe to create src/atanks.res
+ifeq (WIN32,$(PLATFORM))
+  ifneq (,$(findstring /g++,$(CXX)))
+    CXX   := $(shell which mingw32-g++)
+  endif
+  WINDRES := $(shell which windres.exe)
+  MODULES := ${MODULES} obj/atanks.res
+  TARGET  := ${TARGET}.exe
+  RM      := del /q
+endif
+
+# Use the compiler as the linker.
+LD := $(CXX)
+
+# --------------------------------------------------------------------------
+# Determine proper C++11 standard flag, and if and how stack protector works
+# --------------------------------------------------------------------------
+GCCVERSGTEQ47 := 0
+GCCVERSGTEQ49 := 0
+GCCUSESGOLD   := 0
+GCC_STACKPROT :=
+GCC_CXXSTD    := 0x
+PEDANDIC_FLAG := -pedantic
+
+# Note: It has to be evaluated which versions of clang and mingw
+#       start using c++11 instead of c++0x.
+ifneq (,$(findstring /g++,$(CXX)))
+  GCCVERSGTEQ47 := $(shell expr `$(CXX) -dumpversion | cut -f1,2 -d. | tr -d '.'` \>= 47)
+  GCCVERSGTEQ49 := $(shell expr `$(CXX) -dumpversion | cut -f1,2 -d. | tr -d '.'` \>= 49)
+endif
+
+ifeq "$(GCCVERSGTEQ47)" "1"
+  GCC_CXXSTD    := 11
+  PEDANDIC_FLAG := -Wpedantic
+  ifeq "$(GCCVERSGTEQ49)" "1"
+    GCC_STACKPROT := -fstack-protector-strong
+    GCCUSESGOLD   := $(shell ld --version | head -n 1 | grep -c "GNU gold")
+  else
+    GCC_STACKPROT := -fstack-protector
+  endif
+endif
+
+
+# ------------------------------------
+# Flags for compiler and linker
+# ------------------------------------
+CPPFLAGS += -DDATA_DIR=\"${INSTALLDIR}\" -D$(PLATFORM) -DVERSION=\"${VERSION}\"
+CXXFLAGS += -Wall -Wextra $(PEDANDIC_FLAG) -std=c++$(GCC_CXXSTD)
+LDFLAGS  +=
+
+# Depending on the platform, some values have to be appended:
+ifeq (MACOSX,$(PLATFORM))
+  CPPFLAGS := ${CPPFLAGS} -I/usr/local/include $(shell allegro-config --cppflags)
+  LDFLAGS  := ${LDFLAGS} $(shell allegro-config --libs)
+else ifeq (WIN32,$(PLATFORM))
+  CPPFLAGS := ${CPPFLAGS} -I/usr/local/include
+  CXXFLAGS := ${CXXFLAGS} -mwindows
+  LDFLAGS  := ${LDFLAGS} -mwindows -L. -lalleg44
+else
+  ifneq (,$(findstring bsd,$(MAKECMDGOALS)))
+    C_INCLUDE_PATH     := /usr/local/include
+    CPLUS_INCLUDE_PATH := /usr/local/include
+    CXXFLAGS           := ${CXXFLAGS} -Wno-c99-extensions
+    export C_INCLUDE_PATH
+    export CPLUS_INCLUDE_PATH
+  endif
+  CPPFLAGS := ${CPPFLAGS} -DNETWORK $(shell allegro-config --cppflags)
+  CXXFLAGS := ${CXXFLAGS} -pthread
+  LDFLAGS  := ${LDFLAGS} $(shell allegro-config --libs) -lm -lpthread
+endif
+
+
+# If the make goal is "ubuntu", a special define is to be added:
+ifeq (UBUNTU,$(MAKECMDGOALS))
+  CPPFLAGS := ${CPPFLAGS} -DUBUNTU
+endif
+
+
+# ------------------------------------
+# Debug Mode settings
+# ------------------------------------
+HAS_DEBUG_FLAG := NO
+ifneq (,$(findstring -g,$(CXXFLAGS)))
+  ifneq (,$(findstring -ggdb,$(CXXFLAGS)))
+    HAS_DEBUG_FLAG := YES
+  endif
+  DEBUG := YES
+endif
+
+ifeq (YES,$(DEBUG))
+  ifeq (NO,$(HAS_DEBUG_FLAG))
+    CXXFLAGS := -ggdb ${CXXFLAGS} -O0
+  endif
+
+  CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG
+  CXXFLAGS := ${CXXFLAGS} ${GCC_STACKPROT} -Wunused
+
+  # LTO is hard blocked now:
+  USE_LTO := NO
+
+  # address / thread sanitizer activation
+  ifeq (YES,$(SANITIZE_ADDRESS))
+    CXXFLAGS := ${CXXFLAGS} -fsanitize=address
+    LDFLAGS  := ${LDFLAGS} -fsanitize=address
+  else ifeq (YES,$(SANITIZE_LEAK))
+    CXXFLAGS := ${CXXFLAGS} -fsanitize=leak
+    LDFLAGS  := ${LDFLAGS} -fsanitize=leak
+  else ifeq (YES,$(SANITIZE_THREAD))
+    CPPFLAGS := ${CPPFLAGS} -DUSE_MUTEX_INSTEAD_OF_SPINLOCK
+    CXXFLAGS := ${CXXFLAGS} -fsanitize=thread -fPIC -O2 -ggdb
+    LDFLAGS  := ${LDFLAGS} -fsanitize=thread -pie -O2 -ggdb
+  endif
+
+  # Add specific debug message flavours
+  ifeq (YES,$(DEBUG_AICORE))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_AIMING -DATANKS_DEBUG_EMOTIONS
+  endif
+  ifeq (YES,$(DEBUG_AIMING))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_AIMING
+  endif
+  ifeq (YES,$(DEBUG_EMOTION))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_EMOTIONS
+  endif
+  ifeq (YES,$(DEBUG_FINANCE))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_FINANCE
+  endif
+  ifeq (YES,$(DEBUG_OBJECTS))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_OBJECTS
+  endif
+  ifeq (YES,$(DEBUG_PHYSICS))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_PHYSICS
+  endif
+  ifeq (YES,$(DEBUG_LOG_TO_FILE))
+    CPPFLAGS := ${CPPFLAGS} -DATANKS_DEBUG_LOGTOFILE
+  endif
+
+else
+  CXXFLAGS := -march=native ${CXXFLAGS} -O2
+endif
+
+
+# Potentially enable LTO if this is gcc-4.9 and greater
+ifeq (YES,$(USE_LTO))
+  ifeq "$(GCCVERSGTEQ49)" "1"
+    CXXFLAGS := ${CXXFLAGS} -flto
+  endif
+  ifeq "$(GCCUSESGOLD)" "1"
+    CXXFLAGS := ${CXXFLAGS} -fuse-linker-plugin
+  endif
+endif
+
+
+
+# ------------------------------------
+# Distribution file lists
+# ------------------------------------
+DISTCOMMON := \
+atanks/*.dat atanks/COPYING atanks/README atanks/TODO \
+atanks/Changelog atanks/BUGS atanks/*.txt
+
+INCOMMON   := COPYING README TODO Changelog *.txt unicode.dat
+
+# ------------------------------------
+# Default target
+# ------------------------------------
+
+all: $(TARGET)
+
+
+# ------------------------------------
+# Create dependencies
+# This is the standard as described
+# on the GNU make info manual.
+# (See Chapter 4.14)
+# ------------------------------------
+dep/%.d: src/%.cpp
+	@set -e; $(RM) $@; \
+	$(CXX) -MM $(CPPFLAGS) $(CXXFLAGS) $< > $@.$$$$; \
+	$(SED) 's,\($*\)\.o[ :]*,obj/\1.o $@ : ,g' < $@.$$$$ > $@; \
+	$(RM) $@.$$$$
+
+
+# ------------------------------------
+# Compile modules
+# ------------------------------------
+obj/%.o: src/%.cpp
+	@echo "Compiling $@"
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
+
+
+# ------------------------------------
+# Build windows res file
+# ------------------------------------
+obj/atanks.res:
+ifeq (WIN32,$(PLATFORM))
+	$(WINDRES) -i src/atanks.rc --input-format=rc -o obj/atanks.res -O coff
+else
+	@echo "This is no WIN32 platform, so why?"
+endif
+
+
+# ------------------------------------
+# Regular targets
+# ------------------------------------
+install: $(TARGET)
+	$(INSTALL) -d $(DESTDIR)${BINDIR}
 	$(INSTALL) -m 755 atanks $(DESTDIR)${BINDIR}
-	mkdir -p $(DESTDIR)/usr/share/applications
-	$(INSTALL) -m 644 atanks.desktop $(DESTDIR)/usr/share/applications
-	mkdir -p $(DESTDIR)/usr/share/icons/hicolor/48x48/apps
-	$(INSTALL) -m 644 atanks.png $(DESTDIR)/usr/share/icons/hicolor/48x48/apps
-	mkdir -p $(DESTDIR)${INSTALLDIR}
-	mkdir -p $(DESTDIR)${INSTALLDIR}/button
-	mkdir -p $(DESTDIR)${INSTALLDIR}/misc
-	mkdir -p $(DESTDIR)${INSTALLDIR}/missile
-	mkdir -p $(DESTDIR)${INSTALLDIR}/sound
-	mkdir -p $(DESTDIR)${INSTALLDIR}/stock
-	mkdir -p $(DESTDIR)${INSTALLDIR}/tank
-	mkdir -p $(DESTDIR)${INSTALLDIR}/tankgun
-	mkdir -p $(DESTDIR)${INSTALLDIR}/title
-	mkdir -p $(DESTDIR)${INSTALLDIR}/text
+	$(INSTALL) -d $(DESTDIR)$(PREFIX)/share/applications
+	$(INSTALL) -m 644 atanks.desktop $(DESTDIR)$(PREFIX)/share/applications
+	$(INSTALL) -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps
+	$(INSTALL) -m 644 atanks.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/button
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/misc
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/missile
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/sound
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/stock
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/tank
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/tankgun
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/title
+	$(INSTALL) -d $(DESTDIR)${INSTALLDIR}/text
 	$(INSTALL) -m 644 $(INCOMMON) $(DESTDIR)${INSTALLDIR}
 	$(INSTALL) -m 644 button/* $(DESTDIR)${INSTALLDIR}/button
 	$(INSTALL) -m 644 misc/* $(DESTDIR)${INSTALLDIR}/misc
@@ -46,45 +327,61 @@ install:
 	$(INSTALL) -m 644 title/* $(DESTDIR)${INSTALLDIR}/title
 	$(INSTALL) -m 644 text/* $(DESTDIR)${INSTALLDIR}/text
 
-user:
-	INSTALLDIR=./ FLAGS=-DLINUX $(MAKE) -C src
+$(TARGET): $(MODULES)
+	$(LD) -o $@ $(MODULES) $(CPPFLAGS) $(LDFLAGS) $(CXXFLAGS)
 
-winuser:
-	INSTALLDIR=./ FLAGS=-DWIN32 $(MAKE) -C src -f Makefile.windows
+clean:
+	$(RM) obj/*
 
-osxuser:
-	INSTALLDIR=./ FLAGS="-DMACOSX" $(MAKE) -C src -f Makefile.bsd
+veryclean: clean
+ifeq (WIN32,$(PLATFORM))
+	$(RM) $(TARGET).exe
+else
+	$(RM) $(TARGET)
+endif
 
-ubuntu:
-	FLAGS="-DLINUX -DUBUNTU" $(MAKE) -C src
 
-clean:
-	rm -f atanks atanks.exe
-	$(MAKE) -C src clean
+# ------------------------------------
+# User (local) targets
+# ------------------------------------
+
+user: $(TARGET)
+winuser: $(TARGET)
+osxuser: $(TARGET)
+bsduser: $(TARGET)
+ubuntu: $(TARGET)
+
+# ------------------------------------
+# Distribution targets
+# ------------------------------------
 
 dist: source-dist i686-dist win32-dist
 
-tarball: clean
-	cd .. && tar czf atanks-$(VERSION).tar.gz atanks-$(VERSION)
+tarball: veryclean
+	cd .. && tar czf $(FILENAME).tar.gz $(FILENAME) --exclude=.git
 
-zipfile: clean
-	cd .. && zip -r atanks-$(VERSION)-source.zip atanks-$(VERSION)
+zipfile: veryclean
+	cd .. && zip -r $(FILENAME)-source.zip $(FILENAME) -x *.git*
 
-source-dist:
+source-dist: $(TARGET)
 	cd ../; \
-	rm -f ${FILENAME}.tar.gz; \
-	tar cvf ${FILENAME}.tar atanks/src/*.cpp atanks/src/*.h atanks/src/Makefile atanks/src/Makefile.windows atanks/Makefile ${DISTCOMMON}; \
-	gzip ${FILENAME}.tar
+	$(RM) $(FILENAME).tar.gz; \
+	tar czf $(FILENAME).tar.gz atanks/src/*.cpp atanks/src/*.h atanks/Makefile $(DISTCOMMON)
 
-i686-dist:
+i686-dist: $(TARGET)
 	cd ../; \
-	rm -f ${FILENAME}-i686-dist.tar; \
-	rm -f ${FILENAME}-i686-dist.tar.gz; \
+	$(RM) $(FILENAME)-i686-dist.tar.gz; \
 	strip atanks/atanks; \
-	tar cvf ${FILENAME}-i686-dist.tar atanks/atanks ${DISTCOMMON}; \
-	gzip ${FILENAME}-i686-dist.tar;
+	tar czf $(FILENAME)-i686-dist.tar atanks/atanks $(DISTCOMMON)
 
-win32-dist:
+win32-dist: $(TARGET)
 	cd ../; \
-	rm -f ${FILENAME}-win32-dist.zip; \
-	zip ${FILENAME}-win32-dist.zip atanks/Atanks.exe atanks/alleg40.dll ${DISTCOMMON};
+	$(RM) $(FILENAME)-win32-dist.zip; \
+	zip -r $(FILENAME)-win32-dist.zip atanks/atanks.exe atanks/alleg40.dll $(DISTCOMMON)
+
+# ------------------------------------
+# Include all dependency files
+# ------------------------------------
+ifeq (,$(findstring clean,$(MAKECMDGOALS)))
+  -include $(DEPENDS)
+endif
diff --git a/Makefile.bsd b/Makefile.bsd
new file mode 100644
index 0000000..f710e94
--- /dev/null
+++ b/Makefile.bsd
@@ -0,0 +1,298 @@
+.PHONY: all clean veryclean
+
+HERE             := ${.CURDIR}
+MAKEOBJDIRPREFIX := obj
+MODULES := network.o aicore.o debris_pool.o sky.o \
+           teleport.o gfxData.o land.o atanks.o text.o \
+           floattext.o files.o virtobj.o player_types.o \
+           shop.o perlin.o player.o beam.o \
+           optionitemplayer.o satellite.o physobj.o \
+           globaldata.o optionitemcolour.o clock.o menu.o \
+           client.o optiontypes.o tank.o environment.o \
+           decor.o debug.o globaltypes.o missile.o \
+           optionitemmenu.o gameloop.o main.o sound.o \
+           optionitembase.o optionscreens.o button.o \
+           explosion.o update.o
+
+VERSION    := 6.3
+PREFIX     := .
+INSTALLDIR := ${PREFIX}/
+
+CXX ?= clang++
+LD  ?= $(CXX)
+LIB := ar
+
+ALLEGLD  != allegro-config --libs
+CXXFLAGS += -g -std=c++0x -Wall -Wextra -pedantic -Wno-c99-extensions -I../src -I/usr/local/include -pthread
+CPPFLAGS += -DNETWORK -DDATA_DIR=\"${INSTALLDIR}\" -DVERSION=\"${VERSION}\"
+LDFLAGS  :=  $(ALLEGLD) -lm -lpthread
+
+OUTPUT := $(HERE)/atanks
+
+all: $(OUTPUT)
+
+clean:
+	rm -f $(MODULES)
+
+veryclean: clean
+	rm $(OUTPUT)
+
+network.o: ../src/network.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o network.o -c ../src/network.cpp
+aicore.o: ../src/aicore.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o aicore.o -c ../src/aicore.cpp
+debris_pool.o: ../src/debris_pool.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o debris_pool.o -c ../src/debris_pool.cpp
+sky.o: ../src/sky.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o sky.o -c ../src/sky.cpp
+teleport.o: ../src/teleport.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o teleport.o -c ../src/teleport.cpp
+gfxData.o: ../src/gfxData.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o gfxData.o -c ../src/gfxData.cpp
+land.o: ../src/land.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o land.o -c ../src/land.cpp
+atanks.o: ../src/atanks.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o atanks.o -c ../src/atanks.cpp
+text.o: ../src/text.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o text.o -c ../src/text.cpp
+floattext.o: ../src/floattext.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o floattext.o -c ../src/floattext.cpp
+files.o: ../src/files.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o files.o -c ../src/files.cpp
+virtobj.o: ../src/virtobj.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o virtobj.o -c ../src/virtobj.cpp
+player_types.o: ../src/player_types.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o player_types.o -c ../src/player_types.cpp
+shop.o: ../src/shop.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o shop.o -c ../src/shop.cpp
+perlin.o: ../src/perlin.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o perlin.o -c ../src/perlin.cpp
+player.o: ../src/player.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o player.o -c ../src/player.cpp
+beam.o: ../src/beam.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o beam.o -c ../src/beam.cpp
+optionitemplayer.o: ../src/optionitemplayer.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o optionitemplayer.o -c ../src/optionitemplayer.cpp
+satellite.o: ../src/satellite.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o satellite.o -c ../src/satellite.cpp
+physobj.o: ../src/physobj.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o physobj.o -c ../src/physobj.cpp
+globaldata.o: ../src/globaldata.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o globaldata.o -c ../src/globaldata.cpp
+optionitemcolour.o: ../src/optionitemcolour.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o optionitemcolour.o -c ../src/optionitemcolour.cpp
+clock.o: ../src/clock.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o clock.o -c ../src/clock.cpp
+menu.o: ../src/menu.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o menu.o -c ../src/menu.cpp
+client.o: ../src/client.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o client.o -c ../src/client.cpp
+optiontypes.o: ../src/optiontypes.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o optiontypes.o -c ../src/optiontypes.cpp
+tank.o: ../src/tank.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o tank.o -c ../src/tank.cpp
+environment.o: ../src/environment.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o environment.o -c ../src/environment.cpp
+decor.o: ../src/decor.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o decor.o -c ../src/decor.cpp
+debug.o: ../src/debug.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o debug.o -c ../src/debug.cpp
+globaltypes.o: ../src/globaltypes.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o globaltypes.o -c ../src/globaltypes.cpp
+missile.o: ../src/missile.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o missile.o -c ../src/missile.cpp
+optionitemmenu.o: ../src/optionitemmenu.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o optionitemmenu.o -c ../src/optionitemmenu.cpp
+gameloop.o: ../src/gameloop.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o gameloop.o -c ../src/gameloop.cpp
+main.o: ../src/main.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o main.o -c ../src/main.cpp
+sound.o: ../src/sound.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o sound.o -c ../src/sound.cpp
+optionitembase.o: ../src/optionitembase.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o optionitembase.o -c ../src/optionitembase.cpp
+optionscreens.o: ../src/optionscreens.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o optionscreens.o -c ../src/optionscreens.cpp
+button.o: ../src/button.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o button.o -c ../src/button.cpp
+explosion.o: ../src/explosion.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o explosion.o -c ../src/explosion.cpp
+update.o: ../src/update.cpp
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o update.o -c ../src/update.cpp
+
+$(OUTPUT): $(MODULES)
+	$(CXX) $(MODULES) -o $(OUTPUT) $(CPPFLAGS) $(LDFLAGS) $(CXXFLAGS)
+
+
+# Dependencies:
+aicore.o : ../src/aicore.cpp ../src/aicore.h ../src/player_types.h ../src/main.h \
+ ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h ../src/floattext.h ../src/virtobj.h ../src/player.h ../src/tank.h \
+ ../src/physobj.h ../src/missile.h ../src/beam.h ../src/explosion.h
+atanks.o : ../src/atanks.cpp ../src/debug.h ../src/globals.h ../src/globaldata.h \
+ ../src/main.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h ../src/text.h \
+ ../src/optionscreens.h ../src/menu.h ../src/optionitem.h ../src/optionitembase.h \
+ ../src/optiontypes.h ../src/optionitemmenu.h ../src/optionitemplayer.h \
+ ../src/button.h ../src/player.h ../src/player_types.h ../src/files.h ../src/update.h \
+ ../src/tank.h ../src/physobj.h ../src/virtobj.h ../src/floattext.h ../src/beam.h \
+ ../src/missile.h ../src/gameloop.h ../src/clock.h ../src/client.h
+beam.o : ../src/beam.cpp ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/physobj.h ../src/virtobj.h \
+ ../src/player.h ../src/player_types.h ../src/decor.h ../src/debris_pool.h ../src/tank.h \
+ ../src/floattext.h ../src/beam.h ../src/explosion.h ../src/sound.h
+button.o : ../src/button.cpp ../src/button.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h ../src/sound.h
+client.o : ../src/client.cpp ../src/button.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h ../src/files.h \
+ ../src/satellite.h ../src/update.h ../src/client.h ../src/beam.h ../src/physobj.h \
+ ../src/virtobj.h ../src/explosion.h ../src/missile.h ../src/teleport.h \
+ ../src/floattext.h ../src/player.h ../src/player_types.h ../src/tank.h ../src/sky.h
+clock.o : ../src/clock.cpp ../src/clock.h ../src/debug.h
+debris_pool.o : ../src/debris_pool.cpp ../src/debris_pool.h ../src/main.h \
+ ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h
+debug.o : ../src/debug.cpp ../src/debug.h
+decor.o : ../src/decor.cpp ../src/decor.h ../src/physobj.h ../src/globaltypes.h \
+ ../src/virtobj.h ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h ../src/debris_pool.h ../src/sound.h ../src/tank.h ../src/floattext.h
+environment.o : ../src/environment.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h ../src/missile.h \
+ ../src/physobj.h ../src/virtobj.h ../src/tank.h ../src/floattext.h ../src/files.h \
+ ../src/sound.h ../src/player.h ../src/player_types.h
+explosion.o : ../src/explosion.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h ../src/explosion.h \
+ ../src/physobj.h ../src/virtobj.h ../src/missile.h ../src/decor.h ../src/debris_pool.h \
+ ../src/tank.h ../src/floattext.h ../src/player.h ../src/player_types.h
+files.o : ../src/files.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h ../src/player.h \
+ ../src/player_types.h ../src/files.h
+floattext.o : ../src/floattext.cpp ../src/floattext.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h ../src/virtobj.h
+gameloop.o : ../src/gameloop.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h ../src/files.h \
+ ../src/satellite.h ../src/update.h ../src/land.h ../src/clock.h ../src/floattext.h \
+ ../src/virtobj.h ../src/tank.h ../src/physobj.h ../src/explosion.h ../src/beam.h \
+ ../src/missile.h ../src/decor.h ../src/debris_pool.h ../src/teleport.h ../src/sky.h \
+ ../src/sound.h ../src/gameloop.h ../src/player.h ../src/player_types.h ../src/aicore.h \
+ ../src/shop.h
+gfxData.o : ../src/gfxData.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h
+globaldata.o : ../src/globaldata.cpp ../src/player.h ../src/player_types.h \
+ ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h ../src/files.h ../src/tank.h ../src/physobj.h ../src/virtobj.h \
+ ../src/floattext.h ../src/sound.h ../src/debris_pool.h
+globaltypes.o : ../src/globaltypes.cpp ../src/globaltypes.h
+land.o : ../src/land.cpp ../src/land.h ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h ../src/files.h ../src/gameloop.h \
+ ../src/player.h ../src/player_types.h
+main.o : ../src/main.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h
+menu.o : ../src/menu.cpp ../src/optioncontent.h ../src/optiontypes.h \
+ ../src/globaltypes.h ../src/optionitemcolour.h ../src/optionitembase.h \
+ ../src/environment.h ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/network.h ../src/gfxData.h ../src/button.h \
+ ../src/menu.h ../src/optionitem.h ../src/optionitemmenu.h ../src/optionitemplayer.h \
+ ../src/player.h ../src/player_types.h ../src/clock.h
+missile.o : ../src/missile.cpp ../src/explosion.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h ../src/physobj.h \
+ ../src/virtobj.h ../src/missile.h ../src/decor.h ../src/debris_pool.h ../src/tank.h \
+ ../src/floattext.h ../src/player.h ../src/player_types.h ../src/beam.h ../src/sound.h \
+ ../src/aicore.h
+network.o : ../src/network.cpp ../src/network.h ../src/player.h ../src/player_types.h \
+ ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/gfxData.h
+optionitembase.o : ../src/optionitembase.cpp ../src/button.h ../src/main.h \
+ ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h ../src/menu.h ../src/optionitem.h ../src/optionitembase.h \
+ ../src/optiontypes.h ../src/optionitemmenu.h ../src/optionitemplayer.h \
+ ../src/floattext.h ../src/virtobj.h
+optionitemcolour.o : ../src/optionitemcolour.cpp ../src/optionitemcolour.h \
+ ../src/optionitembase.h ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/optiontypes.h
+optionitemmenu.o : ../src/optionitemmenu.cpp ../src/optionitemmenu.h \
+ ../src/optionitembase.h ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/optiontypes.h ../src/menu.h \
+ ../src/optionitem.h ../src/optionitemplayer.h ../src/button.h ../src/clock.h
+optionitemplayer.o : ../src/optionitemplayer.cpp ../src/optionitemplayer.h \
+ ../src/optionitembase.h ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/optiontypes.h ../src/player.h \
+ ../src/player_types.h ../src/floattext.h ../src/virtobj.h
+optionscreens.o : ../src/optionscreens.cpp ../src/optionscreens.h ../src/menu.h \
+ ../src/optionitem.h ../src/optionitembase.h ../src/environment.h ../src/main.h \
+ ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/network.h ../src/gfxData.h \
+ ../src/optiontypes.h ../src/optionitemmenu.h ../src/optionitemplayer.h \
+ ../src/button.h ../src/player.h ../src/player_types.h ../src/files.h ../src/sound.h
+optiontypes.o : ../src/optiontypes.cpp ../src/optiontypes.h
+perlin.o : ../src/perlin.cpp ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h
+physobj.o : ../src/physobj.cpp ../src/physobj.h ../src/globaltypes.h ../src/virtobj.h \
+ ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h
+player.o : ../src/player.cpp ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/player.h ../src/player_types.h \
+ ../src/tank.h ../src/physobj.h ../src/virtobj.h ../src/floattext.h ../src/menu.h \
+ ../src/optionitem.h ../src/optionitembase.h ../src/optiontypes.h \
+ ../src/optionitemmenu.h ../src/optionitemplayer.h ../src/button.h ../src/files.h \
+ ../src/missile.h ../src/aicore.h
+player_types.o : ../src/player_types.cpp ../src/player_types.h ../src/main.h \
+ ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h
+satellite.o : ../src/satellite.cpp ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/satellite.h ../src/beam.h \
+ ../src/physobj.h ../src/virtobj.h
+shop.o : ../src/shop.cpp ../src/shop.h ../src/player.h ../src/player_types.h \
+ ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h \
+ ../src/globaldata.h ../src/text.h ../src/environment.h ../src/network.h \
+ ../src/gfxData.h ../src/files.h ../src/gameloop.h
+sky.o : ../src/sky.cpp ../src/externs.h ../src/globaldata.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/text.h ../src/environment.h \
+ ../src/network.h ../src/gfxData.h ../src/sky.h ../src/files.h ../src/gameloop.h \
+ ../src/player.h ../src/player_types.h
+sound.o : ../src/sound.cpp ../src/sound.h ../src/externs.h ../src/globaldata.h \
+ ../src/main.h ../src/debug.h ../src/imagedefs.h ../src/globaltypes.h ../src/text.h \
+ ../src/environment.h ../src/network.h ../src/gfxData.h
+tank.o : ../src/tank.cpp ../src/floattext.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h ../src/virtobj.h \
+ ../src/explosion.h ../src/physobj.h ../src/teleport.h ../src/missile.h ../src/player.h \
+ ../src/player_types.h ../src/beam.h ../src/tank.h ../src/sound.h
+teleport.o : ../src/teleport.cpp ../src/environment.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/network.h ../src/gfxData.h ../src/teleport.h ../src/virtobj.h \
+ ../src/tank.h ../src/physobj.h ../src/floattext.h ../src/sound.h ../src/player.h \
+ ../src/player_types.h
+text.o : ../src/text.cpp ../src/text.h ../src/main.h ../src/debug.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/externs.h ../src/globaldata.h ../src/environment.h \
+ ../src/network.h ../src/gfxData.h
+update.o : ../src/update.cpp ../src/debug.h ../src/update.h ../src/network.h \
+ ../src/externs.h ../src/globaldata.h ../src/main.h ../src/imagedefs.h \
+ ../src/globaltypes.h ../src/text.h ../src/environment.h ../src/gfxData.h
+virtobj.o : ../src/virtobj.cpp ../src/virtobj.h ../src/main.h ../src/debug.h \
+ ../src/imagedefs.h ../src/globaltypes.h ../src/externs.h ../src/globaldata.h \
+ ../src/text.h ../src/environment.h ../src/network.h ../src/gfxData.h
diff --git a/README b/README
index 1239007..d1023f3 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-README file for Atomic Tanks 
+README file for Atomic Tanks
 ============================================
 
 What is Atomic Tanks?
@@ -149,7 +149,7 @@ Use the left and right arrow buttons on your keyboard to
 aim your tank's gun. The up and down arrow keys adjust the
 speed (power) of the shot. The space bar fires the tanks gun. If
 you want to change which weapon you will be using, press the
-TAB button or the BACKSPACE button. The round is over when 
+TAB button or the BACKSPACE button. The round is over when
 there is one tank left standing.
 
 
@@ -167,14 +167,19 @@ SPACEBAR -- Fires weapons and selects/toggles menu items.
 ENTER -- Is similar to pressing the OK button on a menu.
 UP/DOWN arrows -- Adjusts tank power and cycles through menu items. Scrolls on the buying screen.
 LEFT/RIGHT arrows -- Aim the tank's gun. Also buys and sells on the buying screen or adjusts values in menus.
-ESC -- Cancel out of a menu.
+ESC  -- Cancel out of a menu.
 
-F10 -- Tells the computer to take over your tank for the remainder of the round.
-       Also saves games when on the buying screen.
+F1   -- Take a screenshot
+
+F10  -- Tells the computer to take over your tank for the remainder of the round.
+        Also saves games when on the buying screen.
 
 V, v -- The "v" key controls the volume during matches. Pressing lower-case "v"
         decreases the volume and upper-case "V" increases the volume.
 
+~    -- Show/hide the scoreboard during rounds. On german (and possibly other)
+        keyboards the scoreboard is switched with the key '#'.
+
 
 
 
@@ -237,7 +242,7 @@ Atomic Tanks is a great game, but it isn't perfect.
 We, the developers, are always trying to improve the
 experience our game brings to you. If you have
 a problem with installing or playing Atomic Tanks,
-please let us know. Mail me at jessefrgsmith at yahoo.ca 
+please let us know. Mail me at jessefrgsmith at yahoo.ca
 We encourage you to give your feedback. Please give
 as many details as possible so we can help you.
 
diff --git a/TODO b/TODO
index 05f7dc5..be3d592 100644
--- a/TODO
+++ b/TODO
@@ -5,20 +5,32 @@ lower priority at the bottom. It is currently only vaguely correct.
 	Bugs:
 	- Player options can become scrambled
           if accessed too often.
+          -> Should be fixed with atanks-5.9_aiu1
 	- Wrap around ceiling should not destroy projectile.
+          -> Of course it should. Wrapping is only horizontal, not vertical.
+          -> Added a new option to enable that in atanks-6.0_aiu8
 	- Make sure network client doesn't get
           unlimited shots.
 	- Atanks crashes when Windows players click the Network option
           in the main Options menu.
+          -> Should be fixed with atanks-5.9_aiu1
 	- When cycling through tanks on Players screen,
 	  the old tank does not get erased.
+          -> Should be fixed with atanks-5.9_aiu1
+	- AI controlled tanks buy a limited number of defensive
+          upgrades during very long games. Plating, for example.
 
+In player.cpp the PLAYER::computerSelectItem selects a weapon or item for the bot to use.
+Values less than WEAPONS are weapons and values from WEAPONS to THINGS are items.
+After lots of black magic to select a weapon or item, finally in line 3567 the current_weapon containing a value from 0 to THINGS is passed to PLAYER::computerSelectTarget, which in line 3189 uses it as index into the array weapon which only has size WEAPONS, resulting in a segfault.
+Line 3561 also calls PLAYER::computerSelectTarget with a value from 0 to THINGS, so probably the same can happen there.
+I don't know how to fix this, since I don't know how PLAYER::computerSelectTarget should behave when an item is passed instead of a weapon, but probaly some of you know this and can fix.
+          -> This is fixed in atanks-6.1_aiu2
 
 	Features:
 	- Add scroll bar to buying screen.
 	- Add randomize button to buying screen to have
 	  items automatically purchased.
-
    	- Field repair kit: Spend a turn to repair your tank 
           rather than fire.           
           Limited uses, heals more than Auto Repair Kit 
@@ -27,10 +39,13 @@ lower priority at the bottom. It is currently only vaguely correct.
           its heat signature 
           Yield: Large missile
 	- Better AI against tanks with SDI.
+          -> Added in atanks-6.1_aiu3
 	- Update client ground surface more often.
 	- When switching languages, player menu should display correct text.
+          -> Should be fixed with atanks-5.9_aiu1
 	- Client needs a buying screen.
 	- Make tank size variable.
+          -> It is in atanks-6.0_aiu? Don't remember which...
 	- Add rocks as semi-destructable items.
 
 
@@ -51,8 +66,10 @@ lower priority at the bottom. It is currently only vaguely correct.
 
 	-- Make main window scalable
            (Needs to wait until Allegro 5.x)
+           -> Allegro 5 is a no-opt. I gave up on it but will try SFML-2 soon.
 
 	-- Harder ground
+           -> What is that supposed to mean?
 
 	-- High voltage missiles (discharge on impact or when within range)
 		(Bharat Dhareshwar)
diff --git a/allegro.cfg b/allegro.cfg
new file mode 100644
index 0000000..118b6a8
--- /dev/null
+++ b/allegro.cfg
@@ -0,0 +1,2 @@
+[graphics]
+disable_vsync = yes
diff --git a/allegro.supp b/allegro.supp
new file mode 100644
index 0000000..670a1c2
--- /dev/null
+++ b/allegro.supp
@@ -0,0 +1,238 @@
+{
+   Ignore_glibc_conditional_jump
+   Memcheck:Cond
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:dl_*
+   fun:_dl_*
+   fun:_dl_*
+   obj:/lib64/ld-*.so
+}
+
+{
+   Ignore_install_sound_mempool_called
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_mempool_called_short
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_mempool_called_short_def
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_mempool_called_short_noobj_def
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_mempool_called_mid
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_mempool_inside
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:snd_pcm_open
+   obj:*
+   fun:install_*
+}
+
+{
+   Ignore_install_sound_pcm_open_config_load
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:snd_pcm_open
+}
+
+{
+   Ignore_install_sound_pcm_open_config_parse
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:snd_config_*
+}
+
+{
+   Ignore_snd_open_on_unknown_obj
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:snd_pcm_open
+   obj:*
+}
+
+{
+   Ignore_install_from_main_with_obj_def
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_from_main_with_obj
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_install_from_init_game_setting_with_obj
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   fun:*
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+}
+
+{
+   Ignore_allegro_back_pool
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:malloc
+   fun:_al_malloc
+   obj:*
+   fun:install_*
+   fun:_Z18init_game_settingsP10GLOBALDATA
+   fun:main
+}
+
+{
+   Ignore_X_init_realloc
+   Memcheck:Leak
+   match-leak-kinds: definite
+   fun:realloc
+   fun:add_codeset.isra.10
+   fun:load_generic
+   fun:initialize
+   fun:_XlcCreateLC
+   fun:_XlcDefaultLoader
+   fun:_XOpenLC
+   fun:_XrmInitParseInfo
+   fun:NewDatabase
+   fun:XrmGetStringDatabase
+   fun:InitDefaults
+   fun:XGetDefault
+}
diff --git a/cb/atanks.cbp b/cb/atanks.cbp
new file mode 100644
index 0000000..4d885da
--- /dev/null
+++ b/cb/atanks.cbp
@@ -0,0 +1,438 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="atanks" />
+		<Option makefile_is_custom="1" />
+		<Option execution_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="veryclean">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-O2" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+				</Linker>
+				<MakeCommands>
+					<Build command="$make -f $makefile $target" />
+					<CompileFile command="$make -f $makefile $file" />
+					<Clean command="$make -f $makefile clean" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target" />
+					<SilentBuild command="$make -f $makefile $target > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="user">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile $target" />
+					<CompileFile command="$make -f $makefile $file" />
+					<Clean command="$make -f $makefile veryclean" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target" />
+					<SilentBuild command="$make -j17 -f $makefile $target > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="user LTO">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user USE_LTO=YES" />
+					<CompileFile command="$make -f $makefile $file USE_LTO=YES" />
+					<Clean command="$make -f $makefile veryclean USE_LTO=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile user USE_LTO=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user USE_LTO=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="user ggdb3">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command='CXXFLAGS="-march=native -ggdb3 -O2" $make -j17 -f $makefile user' />
+					<CompileFile command='CXXFLAGS="-march=native -ggdb3 -O2" $make -f $makefile $file' />
+					<Clean command='CXXFLAGS="-march=native -ggdb3 -O2" $make -f $makefile veryclean' />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command='CXXFLAGS="-march=native -ggdb3 -O2" $make -q -f $makefile user' />
+					<SilentBuild command='CXXFLAGS="-march=native -ggdb3 -O2" $make -j17 -f $makefile user > $(CMD_NULL)' />
+				</MakeCommands>
+			</Target>
+			<Target title="user debug">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile user DEBUG=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="AICORE">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_AICORE=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_AICORE=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_AICORE=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="AICORE to Log">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES DEBUG_LOG_TO_FILE=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_AICORE=YES DEBUG_LOG_TO_FILE=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_AICORE=YES DEBUG_LOG_TO_FILE=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_AICORE=YES DEBUG_LOG_TO_FILE=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES DEBUG_LOG_TO_FILE=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="AICORE FIN">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="AICORE FIN to LOG">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES DEBUG_LOG_TO_FILE=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES DEBUG_LOG_TO_FILE=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES DEBUG_LOG_TO_FILE=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES DEBUG_LOG_TO_FILE=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AICORE=YES DEBUG_FINANCE=YES DEBUG_LOG_TO_FILE=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="AIM">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AIMING=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_AIMING=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_AIMING=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_AIMING=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_AIMING=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="EMO">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_EMOTION=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_EMOTION=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_EMOTION=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_EMOTION=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_EMOTION=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="FIN">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_FINANCE=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_FINANCE=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_FINANCE=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_FINANCE=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_FINANCE=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="OBJ">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -f $makefile user DEBUG=YES DEBUG_OBJECTS=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_OBJECTS=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_OBJECTS=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_OBJECTS=YES" />
+					<SilentBuild command="$make -f $makefile user DEBUG=YES DEBUG_OBJECTS=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="PHY">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_PHYSICS=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_PHYSICS=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_PHYSICS=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_PHYSICS=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_PHYSICS=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="FIN PHY">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES DEBUG_FINANCE=YES DEBUG_PHYSICS=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES DEBUG_FINANCE=YES DEBUG_PHYSICS=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES DEBUG_FINANCE=YES DEBUG_PHYSICS=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES DEBUG_FINANCE=YES DEBUG_PHYSICS=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES DEBUG_FINANCE=YES DEBUG_PHYSICS=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="SAN address">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES SANITIZE_ADDRESS=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES SANITIZE_ADDRESS=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES SANITIZE_ADDRESS=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES SANITIZE_ADDRESS=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES SANITIZE_ADDRESS=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="SAN leak">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES SANITIZE_LEAK=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES SANITIZE_LEAK=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES SANITIZE_LEAK=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES SANITIZE_LEAK=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES SANITIZE_LEAK=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+			<Target title="SAN thread">
+				<Option output="/home/sed/pryde/PrydeWorX/atanks_aiu/atanks" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="/home/sed/pryde/PrydeWorX/atanks_aiu" />
+				<Option object_output="../obj/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+				<MakeCommands>
+					<Build command="$make -j17 -f $makefile user DEBUG=YES SANITIZE_THREAD=YES" />
+					<CompileFile command="$make -f $makefile $file DEBUG=YES SANITIZE_THREAD=YES" />
+					<Clean command="$make -f $makefile veryclean DEBUG=YES SANITIZE_THREAD=YES" />
+					<DistClean command="$make -f $makefile distclean$target" />
+					<AskRebuildNeeded command="$make -q -f $makefile $target DEBUG=YES SANITIZE_THREAD=YES" />
+					<SilentBuild command="$make -j17 -f $makefile user DEBUG=YES SANITIZE_THREAD=YES > $(CMD_NULL)" />
+				</MakeCommands>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add option="-Wall" />
+		</Compiler>
+		<Unit filename="../Makefile" />
+		<Unit filename="../Makefile.bsd" />
+		<Unit filename="../TODO_akut" />
+		<Unit filename="../src/aicore.cpp" />
+		<Unit filename="../src/aicore.h" />
+		<Unit filename="../src/atanks.cpp" />
+		<Unit filename="../src/beam.cpp" />
+		<Unit filename="../src/beam.h" />
+		<Unit filename="../src/button.cpp" />
+		<Unit filename="../src/button.h" />
+		<Unit filename="../src/client.cpp" />
+		<Unit filename="../src/client.h" />
+		<Unit filename="../src/clock.cpp" />
+		<Unit filename="../src/clock.h" />
+		<Unit filename="../src/debris_pool.cpp" />
+		<Unit filename="../src/debris_pool.h" />
+		<Unit filename="../src/debug.cpp" />
+		<Unit filename="../src/debug.h" />
+		<Unit filename="../src/decor.cpp" />
+		<Unit filename="../src/decor.h" />
+		<Unit filename="../src/environment.cpp" />
+		<Unit filename="../src/environment.h" />
+		<Unit filename="../src/explosion.cpp" />
+		<Unit filename="../src/explosion.h" />
+		<Unit filename="../src/externs.h" />
+		<Unit filename="../src/files.cpp" />
+		<Unit filename="../src/files.h" />
+		<Unit filename="../src/floattext.cpp" />
+		<Unit filename="../src/floattext.h" />
+		<Unit filename="../src/gameloop.cpp" />
+		<Unit filename="../src/gameloop.h" />
+		<Unit filename="../src/gfxData.cpp" />
+		<Unit filename="../src/gfxData.h" />
+		<Unit filename="../src/globaldata.cpp" />
+		<Unit filename="../src/globaldata.h" />
+		<Unit filename="../src/globals.h" />
+		<Unit filename="../src/globaltypes.cpp" />
+		<Unit filename="../src/globaltypes.h" />
+		<Unit filename="../src/land.cpp" />
+		<Unit filename="../src/land.h" />
+		<Unit filename="../src/main.cpp" />
+		<Unit filename="../src/main.h" />
+		<Unit filename="../src/menu.cpp" />
+		<Unit filename="../src/menu.h" />
+		<Unit filename="../src/missile.cpp" />
+		<Unit filename="../src/missile.h" />
+		<Unit filename="../src/network.cpp" />
+		<Unit filename="../src/network.h" />
+		<Unit filename="../src/optioncontent.h" />
+		<Unit filename="../src/optionitem.h" />
+		<Unit filename="../src/optionitembase.cpp" />
+		<Unit filename="../src/optionitembase.h" />
+		<Unit filename="../src/optionitemcolour.cpp" />
+		<Unit filename="../src/optionitemcolour.h" />
+		<Unit filename="../src/optionitemmenu.cpp" />
+		<Unit filename="../src/optionitemmenu.h" />
+		<Unit filename="../src/optionitemplayer.cpp" />
+		<Unit filename="../src/optionitemplayer.h" />
+		<Unit filename="../src/optionscreens.cpp" />
+		<Unit filename="../src/optionscreens.h" />
+		<Unit filename="../src/optiontypes.cpp" />
+		<Unit filename="../src/optiontypes.h" />
+		<Unit filename="../src/perlin.cpp" />
+		<Unit filename="../src/physobj.cpp" />
+		<Unit filename="../src/physobj.h" />
+		<Unit filename="../src/player.cpp" />
+		<Unit filename="../src/player.h" />
+		<Unit filename="../src/player_types.cpp" />
+		<Unit filename="../src/player_types.h" />
+		<Unit filename="../src/satellite.cpp" />
+		<Unit filename="../src/satellite.h" />
+		<Unit filename="../src/shop.cpp" />
+		<Unit filename="../src/shop.h" />
+		<Unit filename="../src/sky.cpp" />
+		<Unit filename="../src/sky.h" />
+		<Unit filename="../src/sound.cpp" />
+		<Unit filename="../src/sound.h" />
+		<Unit filename="../src/tank.cpp" />
+		<Unit filename="../src/tank.h" />
+		<Unit filename="../src/teleport.cpp" />
+		<Unit filename="../src/teleport.h" />
+		<Unit filename="../src/text.cpp" />
+		<Unit filename="../src/text.h" />
+		<Unit filename="../src/update.cpp" />
+		<Unit filename="../src/update.h" />
+		<Unit filename="../src/virtobj.cpp" />
+		<Unit filename="../src/virtobj.h" />
+		<Unit filename="../src/winclock.h" />
+		<Unit filename="../text/weapons.txt" />
+		<Extensions>
+			<envvars />
+			<code_completion />
+			<debugger />
+			<lib_finder disable_auto="1" />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>
diff --git a/cb/atanks.workspace b/cb/atanks.workspace
new file mode 100644
index 0000000..89c6a72
--- /dev/null
+++ b/cb/atanks.workspace
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_workspace_file>
+	<Workspace title="Atomic Tanks">
+		<Project filename="atanks.cbp" />
+	</Workspace>
+</CodeBlocks_workspace_file>
diff --git a/credits.txt b/credits.txt
index 62e5dd0..3ed7804 100644
--- a/credits.txt
+++ b/credits.txt
@@ -7,6 +7,8 @@ Jesse Smith (jessefrgsmith at yahoo.ca)
 Mike Wilson (CaptainNeeda+atanks at gmail.com)
 Kevin (sylikc at gmail.com)
 Sven Eden (yamakuzure at users.sourceforge.net)
+Bruno Victal
+Bill Buerger
 
 Graphics
 Tom Hudson (tom at singular.org.uk)
diff --git a/dep/.keep_dir b/dep/.keep_dir
new file mode 100644
index 0000000..e69de29
diff --git a/dep/aicore.d b/dep/aicore.d
new file mode 100644
index 0000000..1acdde0
--- /dev/null
+++ b/dep/aicore.d
@@ -0,0 +1,5 @@
+obj/aicore.o dep/aicore.d : src/aicore.cpp src/aicore.h src/player_types.h src/main.h \
+ src/debug.h src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/floattext.h \
+ src/virtobj.h src/player.h src/tank.h src/physobj.h src/missile.h \
+ src/beam.h src/explosion.h
diff --git a/dep/atanks.d b/dep/atanks.d
new file mode 100644
index 0000000..6b243dd
--- /dev/null
+++ b/dep/atanks.d
@@ -0,0 +1,8 @@
+obj/atanks.o dep/atanks.d : src/atanks.cpp src/debug.h src/globals.h src/globaldata.h \
+ src/main.h src/globaltypes.h src/externs.h src/environment.h \
+ src/network.h src/gfxData.h src/text.h src/optionscreens.h src/menu.h \
+ src/optionitem.h src/optionitembase.h src/optiontypes.h \
+ src/optionitemmenu.h src/optionitemplayer.h src/button.h src/player.h \
+ src/player_types.h src/files.h src/update.h src/tank.h src/physobj.h \
+ src/virtobj.h src/floattext.h src/beam.h src/missile.h src/gameloop.h \
+ src/clock.h src/client.h
diff --git a/dep/beam.d b/dep/beam.d
new file mode 100644
index 0000000..c087c34
--- /dev/null
+++ b/dep/beam.d
@@ -0,0 +1,5 @@
+obj/beam.o dep/beam.d : src/beam.cpp src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/physobj.h src/virtobj.h src/player.h \
+ src/player_types.h src/decor.h src/debris_pool.h src/tank.h \
+ src/floattext.h src/beam.h src/explosion.h src/sound.h
diff --git a/dep/button.d b/dep/button.d
new file mode 100644
index 0000000..145cd74
--- /dev/null
+++ b/dep/button.d
@@ -0,0 +1,3 @@
+obj/button.o dep/button.d : src/button.cpp src/button.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/sound.h
diff --git a/dep/client.d b/dep/client.d
new file mode 100644
index 0000000..3f6222c
--- /dev/null
+++ b/dep/client.d
@@ -0,0 +1,6 @@
+obj/client.o dep/client.d : src/client.cpp src/button.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/files.h \
+ src/satellite.h src/update.h src/client.h src/beam.h src/physobj.h \
+ src/virtobj.h src/explosion.h src/missile.h src/teleport.h \
+ src/floattext.h src/player.h src/player_types.h src/tank.h src/sky.h
diff --git a/dep/clock.d b/dep/clock.d
new file mode 100644
index 0000000..39b0138
--- /dev/null
+++ b/dep/clock.d
@@ -0,0 +1 @@
+obj/clock.o dep/clock.d : src/clock.cpp src/clock.h src/debug.h
diff --git a/dep/debris_pool.d b/dep/debris_pool.d
new file mode 100644
index 0000000..7f81bb4
--- /dev/null
+++ b/dep/debris_pool.d
@@ -0,0 +1,3 @@
+obj/debris_pool.o dep/debris_pool.d : src/debris_pool.cpp src/debris_pool.h src/main.h \
+ src/debug.h src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h
diff --git a/dep/debug.d b/dep/debug.d
new file mode 100644
index 0000000..4ec2261
--- /dev/null
+++ b/dep/debug.d
@@ -0,0 +1 @@
+obj/debug.o dep/debug.d : src/debug.cpp src/debug.h
diff --git a/dep/decor.d b/dep/decor.d
new file mode 100644
index 0000000..5e1846e
--- /dev/null
+++ b/dep/decor.d
@@ -0,0 +1,4 @@
+obj/decor.o dep/decor.d : src/decor.cpp src/decor.h src/physobj.h src/globaltypes.h \
+ src/virtobj.h src/main.h src/debug.h src/externs.h src/globaldata.h \
+ src/text.h src/environment.h src/network.h src/gfxData.h \
+ src/debris_pool.h src/sound.h src/tank.h src/floattext.h
diff --git a/dep/environment.d b/dep/environment.d
new file mode 100644
index 0000000..54ce493
--- /dev/null
+++ b/dep/environment.d
@@ -0,0 +1,5 @@
+obj/environment.o dep/environment.d : src/environment.cpp src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/missile.h \
+ src/physobj.h src/virtobj.h src/tank.h src/floattext.h src/files.h \
+ src/sound.h src/player.h src/player_types.h
diff --git a/dep/explosion.d b/dep/explosion.d
new file mode 100644
index 0000000..9f62343
--- /dev/null
+++ b/dep/explosion.d
@@ -0,0 +1,5 @@
+obj/explosion.o dep/explosion.d : src/explosion.cpp src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h src/explosion.h src/physobj.h src/virtobj.h \
+ src/missile.h src/decor.h src/debris_pool.h src/tank.h src/floattext.h \
+ src/player.h src/player_types.h
diff --git a/dep/files.d b/dep/files.d
new file mode 100644
index 0000000..b488329
--- /dev/null
+++ b/dep/files.d
@@ -0,0 +1,3 @@
+obj/files.o dep/files.d : src/files.cpp src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h src/player.h src/player_types.h src/files.h
diff --git a/dep/floattext.d b/dep/floattext.d
new file mode 100644
index 0000000..4df50a5
--- /dev/null
+++ b/dep/floattext.d
@@ -0,0 +1,3 @@
+obj/floattext.o dep/floattext.d : src/floattext.cpp src/floattext.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/virtobj.h
diff --git a/dep/gameloop.d b/dep/gameloop.d
new file mode 100644
index 0000000..86365c8
--- /dev/null
+++ b/dep/gameloop.d
@@ -0,0 +1,7 @@
+obj/gameloop.o dep/gameloop.d : src/gameloop.cpp src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h src/files.h src/satellite.h src/update.h \
+ src/land.h src/clock.h src/floattext.h src/virtobj.h src/tank.h \
+ src/physobj.h src/explosion.h src/beam.h src/missile.h src/decor.h \
+ src/debris_pool.h src/teleport.h src/sky.h src/sound.h src/gameloop.h \
+ src/player.h src/player_types.h src/aicore.h src/shop.h
diff --git a/dep/gfxData.d b/dep/gfxData.d
new file mode 100644
index 0000000..827498c
--- /dev/null
+++ b/dep/gfxData.d
@@ -0,0 +1,3 @@
+obj/gfxData.o dep/gfxData.d : src/gfxData.cpp src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h
diff --git a/dep/globaldata.d b/dep/globaldata.d
new file mode 100644
index 0000000..36e6b37
--- /dev/null
+++ b/dep/globaldata.d
@@ -0,0 +1,5 @@
+obj/globaldata.o dep/globaldata.d : src/globaldata.cpp src/player.h src/player_types.h \
+ src/main.h src/debug.h src/globaltypes.h src/externs.h src/globaldata.h \
+ src/text.h src/environment.h src/network.h src/gfxData.h src/files.h \
+ src/tank.h src/physobj.h src/virtobj.h src/floattext.h src/sound.h \
+ src/debris_pool.h
diff --git a/dep/globaltypes.d b/dep/globaltypes.d
new file mode 100644
index 0000000..5548261
--- /dev/null
+++ b/dep/globaltypes.d
@@ -0,0 +1 @@
+obj/globaltypes.o dep/globaltypes.d : src/globaltypes.cpp src/globaltypes.h
diff --git a/dep/land.d b/dep/land.d
new file mode 100644
index 0000000..46a24b0
--- /dev/null
+++ b/dep/land.d
@@ -0,0 +1,4 @@
+obj/land.o dep/land.d : src/land.cpp src/land.h src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h src/files.h src/gameloop.h src/player.h \
+ src/player_types.h
diff --git a/dep/main.d b/dep/main.d
new file mode 100644
index 0000000..e2fa4fe
--- /dev/null
+++ b/dep/main.d
@@ -0,0 +1,3 @@
+obj/main.o dep/main.d : src/main.cpp src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h
diff --git a/dep/menu.d b/dep/menu.d
new file mode 100644
index 0000000..300f35d
--- /dev/null
+++ b/dep/menu.d
@@ -0,0 +1,6 @@
+obj/menu.o dep/menu.d : src/menu.cpp src/optioncontent.h src/optiontypes.h \
+ src/globaltypes.h src/optionitemcolour.h src/optionitembase.h \
+ src/environment.h src/main.h src/debug.h src/externs.h src/globaldata.h \
+ src/text.h src/network.h src/gfxData.h src/button.h src/menu.h \
+ src/optionitem.h src/optionitemmenu.h src/optionitemplayer.h \
+ src/player.h src/player_types.h src/clock.h
diff --git a/dep/missile.d b/dep/missile.d
new file mode 100644
index 0000000..d7dac69
--- /dev/null
+++ b/dep/missile.d
@@ -0,0 +1,6 @@
+obj/missile.o dep/missile.d : src/missile.cpp src/explosion.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/physobj.h \
+ src/virtobj.h src/missile.h src/decor.h src/debris_pool.h src/tank.h \
+ src/floattext.h src/player.h src/player_types.h src/beam.h src/sound.h \
+ src/aicore.h
diff --git a/dep/network.d b/dep/network.d
new file mode 100644
index 0000000..ad310b6
--- /dev/null
+++ b/dep/network.d
@@ -0,0 +1,3 @@
+obj/network.o dep/network.d : src/network.cpp src/network.h src/player.h src/player_types.h \
+ src/main.h src/debug.h src/globaltypes.h src/externs.h src/globaldata.h \
+ src/text.h src/environment.h src/gfxData.h
diff --git a/dep/optionitembase.d b/dep/optionitembase.d
new file mode 100644
index 0000000..c533418
--- /dev/null
+++ b/dep/optionitembase.d
@@ -0,0 +1,6 @@
+obj/optionitembase.o dep/optionitembase.d : src/optionitembase.cpp src/button.h src/main.h \
+ src/debug.h src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/menu.h \
+ src/optionitem.h src/optionitembase.h src/optiontypes.h \
+ src/optionitemmenu.h src/optionitemplayer.h src/floattext.h \
+ src/virtobj.h
diff --git a/dep/optionitemcolour.d b/dep/optionitemcolour.d
new file mode 100644
index 0000000..e43bd25
--- /dev/null
+++ b/dep/optionitemcolour.d
@@ -0,0 +1,4 @@
+obj/optionitemcolour.o dep/optionitemcolour.d : src/optionitemcolour.cpp src/optionitemcolour.h \
+ src/optionitembase.h src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/optiontypes.h
diff --git a/dep/optionitemmenu.d b/dep/optionitemmenu.d
new file mode 100644
index 0000000..d11bd0a
--- /dev/null
+++ b/dep/optionitemmenu.d
@@ -0,0 +1,5 @@
+obj/optionitemmenu.o dep/optionitemmenu.d : src/optionitemmenu.cpp src/optionitemmenu.h \
+ src/optionitembase.h src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/optiontypes.h src/menu.h \
+ src/optionitem.h src/optionitemplayer.h src/button.h src/clock.h
diff --git a/dep/optionitemplayer.d b/dep/optionitemplayer.d
new file mode 100644
index 0000000..be5458b
--- /dev/null
+++ b/dep/optionitemplayer.d
@@ -0,0 +1,5 @@
+obj/optionitemplayer.o dep/optionitemplayer.d : src/optionitemplayer.cpp src/optionitemplayer.h \
+ src/optionitembase.h src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/optiontypes.h src/player.h \
+ src/player_types.h src/floattext.h src/virtobj.h
diff --git a/dep/optionscreens.d b/dep/optionscreens.d
new file mode 100644
index 0000000..fcfc44a
--- /dev/null
+++ b/dep/optionscreens.d
@@ -0,0 +1,6 @@
+obj/optionscreens.o dep/optionscreens.d : src/optionscreens.cpp src/optionscreens.h src/menu.h \
+ src/optionitem.h src/optionitembase.h src/environment.h src/main.h \
+ src/debug.h src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/optiontypes.h src/optionitemmenu.h \
+ src/optionitemplayer.h src/button.h src/player.h src/player_types.h \
+ src/files.h src/sound.h
diff --git a/dep/optiontypes.d b/dep/optiontypes.d
new file mode 100644
index 0000000..b6d2618
--- /dev/null
+++ b/dep/optiontypes.d
@@ -0,0 +1 @@
+obj/optiontypes.o dep/optiontypes.d : src/optiontypes.cpp src/optiontypes.h
diff --git a/dep/perlin.d b/dep/perlin.d
new file mode 100644
index 0000000..44d39b2
--- /dev/null
+++ b/dep/perlin.d
@@ -0,0 +1,3 @@
+obj/perlin.o dep/perlin.d : src/perlin.cpp src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h
diff --git a/dep/physobj.d b/dep/physobj.d
new file mode 100644
index 0000000..7c9e336
--- /dev/null
+++ b/dep/physobj.d
@@ -0,0 +1,3 @@
+obj/physobj.o dep/physobj.d : src/physobj.cpp src/physobj.h src/globaltypes.h src/virtobj.h \
+ src/main.h src/debug.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h
diff --git a/dep/player.d b/dep/player.d
new file mode 100644
index 0000000..381fe45
--- /dev/null
+++ b/dep/player.d
@@ -0,0 +1,7 @@
+obj/player.o dep/player.d : src/player.cpp src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/player.h src/player_types.h src/tank.h \
+ src/physobj.h src/virtobj.h src/floattext.h src/menu.h src/optionitem.h \
+ src/optionitembase.h src/optiontypes.h src/optionitemmenu.h \
+ src/optionitemplayer.h src/button.h src/files.h src/missile.h \
+ src/aicore.h
diff --git a/dep/player_types.d b/dep/player_types.d
new file mode 100644
index 0000000..f23b730
--- /dev/null
+++ b/dep/player_types.d
@@ -0,0 +1,3 @@
+obj/player_types.o dep/player_types.d : src/player_types.cpp src/player_types.h src/main.h \
+ src/debug.h src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h
diff --git a/dep/satellite.d b/dep/satellite.d
new file mode 100644
index 0000000..f485bd2
--- /dev/null
+++ b/dep/satellite.d
@@ -0,0 +1,4 @@
+obj/satellite.o dep/satellite.d : src/satellite.cpp src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/satellite.h src/beam.h src/physobj.h \
+ src/virtobj.h
diff --git a/dep/shop.d b/dep/shop.d
new file mode 100644
index 0000000..b84a1bf
--- /dev/null
+++ b/dep/shop.d
@@ -0,0 +1,4 @@
+obj/shop.o dep/shop.d : src/shop.cpp src/shop.h src/player.h src/player_types.h \
+ src/main.h src/debug.h src/globaltypes.h src/externs.h src/globaldata.h \
+ src/text.h src/environment.h src/network.h src/gfxData.h src/files.h \
+ src/gameloop.h
diff --git a/dep/sky.d b/dep/sky.d
new file mode 100644
index 0000000..7385e85
--- /dev/null
+++ b/dep/sky.d
@@ -0,0 +1,4 @@
+obj/sky.o dep/sky.d : src/sky.cpp src/externs.h src/globaldata.h src/main.h src/debug.h \
+ src/globaltypes.h src/text.h src/environment.h src/network.h \
+ src/gfxData.h src/sky.h src/files.h src/gameloop.h src/player.h \
+ src/player_types.h
diff --git a/dep/sound.d b/dep/sound.d
new file mode 100644
index 0000000..9b54b70
--- /dev/null
+++ b/dep/sound.d
@@ -0,0 +1,3 @@
+obj/sound.o dep/sound.d : src/sound.cpp src/sound.h src/externs.h src/globaldata.h \
+ src/main.h src/debug.h src/globaltypes.h src/text.h src/environment.h \
+ src/network.h src/gfxData.h
diff --git a/dep/tank.d b/dep/tank.d
new file mode 100644
index 0000000..6209f26
--- /dev/null
+++ b/dep/tank.d
@@ -0,0 +1,5 @@
+obj/tank.o dep/tank.d : src/tank.cpp src/floattext.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h src/virtobj.h \
+ src/explosion.h src/physobj.h src/teleport.h src/missile.h src/player.h \
+ src/player_types.h src/beam.h src/tank.h src/sound.h
diff --git a/dep/teleport.d b/dep/teleport.d
new file mode 100644
index 0000000..d2ce83e
--- /dev/null
+++ b/dep/teleport.d
@@ -0,0 +1,5 @@
+obj/teleport.o dep/teleport.d : src/teleport.cpp src/environment.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/network.h src/gfxData.h src/teleport.h src/virtobj.h src/tank.h \
+ src/physobj.h src/floattext.h src/sound.h src/player.h \
+ src/player_types.h
diff --git a/dep/text.d b/dep/text.d
new file mode 100644
index 0000000..8aa6bcf
--- /dev/null
+++ b/dep/text.d
@@ -0,0 +1,3 @@
+obj/text.o dep/text.d : src/text.cpp src/text.h src/main.h src/debug.h src/globaltypes.h \
+ src/externs.h src/globaldata.h src/environment.h src/network.h \
+ src/gfxData.h
diff --git a/dep/update.d b/dep/update.d
new file mode 100644
index 0000000..6cddf67
--- /dev/null
+++ b/dep/update.d
@@ -0,0 +1,3 @@
+obj/update.o dep/update.d : src/update.cpp src/debug.h src/update.h src/network.h \
+ src/externs.h src/globaldata.h src/main.h src/globaltypes.h src/text.h \
+ src/environment.h src/gfxData.h
diff --git a/dep/virtobj.d b/dep/virtobj.d
new file mode 100644
index 0000000..d37876a
--- /dev/null
+++ b/dep/virtobj.d
@@ -0,0 +1,3 @@
+obj/virtobj.o dep/virtobj.d : src/virtobj.cpp src/virtobj.h src/main.h src/debug.h \
+ src/globaltypes.h src/externs.h src/globaldata.h src/text.h \
+ src/environment.h src/network.h src/gfxData.h
diff --git a/do_helgrind.sh b/do_helgrind.sh
new file mode 100755
index 0000000..ff8744f
--- /dev/null
+++ b/do_helgrind.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+/usr/bin/valgrind -v --trace-children=yes --tool=helgrind --read-var-info=yes ./atanks
+
+
diff --git a/do_memcheck.sh b/do_memcheck.sh
new file mode 100755
index 0000000..e8f5efb
--- /dev/null
+++ b/do_memcheck.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+/usr/bin/valgrind -v --trace-children=yes --tool=memcheck \
+--track-origins=yes --leak-check=full --show-reachable=no \
+--read-var-info=yes ./atanks
+
+
diff --git a/gdb_memcheck.sh b/gdb_memcheck.sh
new file mode 100755
index 0000000..040b743
--- /dev/null
+++ b/gdb_memcheck.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+/usr/bin/valgrind -v --trace-children=yes --tool=memcheck \
+--track-origins=yes --leak-check=full --show-reachable=no \
+--read-var-info=yes --vgdb=full --vgdb-error=0 ./atanks
+
+
diff --git a/obj/.keep_dir b/obj/.keep_dir
new file mode 100644
index 0000000..e69de29
diff --git a/sound/0.wav b/sound/0.wav
deleted file mode 100644
index be6007b..0000000
Binary files a/sound/0.wav and /dev/null differ
diff --git a/sound/00.wav b/sound/00.wav
new file mode 100644
index 0000000..993d72b
Binary files /dev/null and b/sound/00.wav differ
diff --git a/sound/01.wav b/sound/01.wav
new file mode 100644
index 0000000..4cf2c00
Binary files /dev/null and b/sound/01.wav differ
diff --git a/sound/02.wav b/sound/02.wav
new file mode 100644
index 0000000..199f802
Binary files /dev/null and b/sound/02.wav differ
diff --git a/sound/03.wav b/sound/03.wav
new file mode 100644
index 0000000..2429644
Binary files /dev/null and b/sound/03.wav differ
diff --git a/sound/04.wav b/sound/04.wav
new file mode 100644
index 0000000..efefa7d
Binary files /dev/null and b/sound/04.wav differ
diff --git a/sound/05.wav b/sound/05.wav
new file mode 100644
index 0000000..662ec1b
Binary files /dev/null and b/sound/05.wav differ
diff --git a/sound/06.wav b/sound/06.wav
new file mode 100644
index 0000000..2cd3f5e
Binary files /dev/null and b/sound/06.wav differ
diff --git a/sound/07.wav b/sound/07.wav
new file mode 100644
index 0000000..6bc1253
Binary files /dev/null and b/sound/07.wav differ
diff --git a/sound/1.wav b/sound/1.wav
deleted file mode 100644
index 0ed9d07..0000000
Binary files a/sound/1.wav and /dev/null differ
diff --git a/sound/10.wav b/sound/10.wav
index 9ae290c..da5d044 100644
Binary files a/sound/10.wav and b/sound/10.wav differ
diff --git a/sound/11.wav b/sound/11.wav
index c2ee04d..5528fac 100644
Binary files a/sound/11.wav and b/sound/11.wav differ
diff --git a/sound/12.wav b/sound/12.wav
index a52bc4e..4ab1dd7 100644
Binary files a/sound/12.wav and b/sound/12.wav differ
diff --git a/sound/13.wav b/sound/13.wav
new file mode 100644
index 0000000..479c4fb
Binary files /dev/null and b/sound/13.wav differ
diff --git a/sound/14.wav b/sound/14.wav
new file mode 100644
index 0000000..79f38bb
Binary files /dev/null and b/sound/14.wav differ
diff --git a/sound/15.wav b/sound/15.wav
new file mode 100644
index 0000000..0b45dcb
Binary files /dev/null and b/sound/15.wav differ
diff --git a/sound/16.wav b/sound/16.wav
new file mode 100644
index 0000000..cd5fd08
Binary files /dev/null and b/sound/16.wav differ
diff --git a/sound/17.wav b/sound/17.wav
new file mode 100644
index 0000000..254bea2
Binary files /dev/null and b/sound/17.wav differ
diff --git a/sound/18.wav b/sound/18.wav
new file mode 100644
index 0000000..50f81f3
Binary files /dev/null and b/sound/18.wav differ
diff --git a/sound/19.wav b/sound/19.wav
new file mode 100644
index 0000000..ff5d133
Binary files /dev/null and b/sound/19.wav differ
diff --git a/sound/2.wav b/sound/2.wav
deleted file mode 100644
index 86200ee..0000000
Binary files a/sound/2.wav and /dev/null differ
diff --git a/sound/20.wav b/sound/20.wav
new file mode 100644
index 0000000..41e20e6
Binary files /dev/null and b/sound/20.wav differ
diff --git a/sound/21.wav b/sound/21.wav
new file mode 100644
index 0000000..f3b1cea
Binary files /dev/null and b/sound/21.wav differ
diff --git a/sound/22.wav b/sound/22.wav
new file mode 100644
index 0000000..7ad0c48
Binary files /dev/null and b/sound/22.wav differ
diff --git a/sound/3.wav b/sound/3.wav
deleted file mode 100644
index fab1992..0000000
Binary files a/sound/3.wav and /dev/null differ
diff --git a/sound/30.wav b/sound/30.wav
new file mode 100644
index 0000000..7569fac
Binary files /dev/null and b/sound/30.wav differ
diff --git a/sound/31.wav b/sound/31.wav
new file mode 100644
index 0000000..45faf55
Binary files /dev/null and b/sound/31.wav differ
diff --git a/sound/10.wav b/sound/32.wav
similarity index 100%
copy from sound/10.wav
copy to sound/32.wav
diff --git a/sound/4.wav b/sound/4.wav
deleted file mode 100644
index 3f74387..0000000
Binary files a/sound/4.wav and /dev/null differ
diff --git a/sound/8.wav b/sound/40.wav
similarity index 100%
rename from sound/8.wav
rename to sound/40.wav
diff --git a/sound/5.wav b/sound/5.wav
deleted file mode 100644
index b66b9e4..0000000
Binary files a/sound/5.wav and /dev/null differ
diff --git a/sound/6.wav b/sound/6.wav
deleted file mode 100644
index 6038f20..0000000
Binary files a/sound/6.wav and /dev/null differ
diff --git a/sound/7.wav b/sound/7.wav
deleted file mode 100644
index e3f4e63..0000000
Binary files a/sound/7.wav and /dev/null differ
diff --git a/sound/9.wav b/sound/9.wav
deleted file mode 100644
index 845db24..0000000
Binary files a/sound/9.wav and /dev/null differ
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index e2aa5a1..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,149 +0,0 @@
-.PHONY: all clean veryclean
-
-MODULES = atanks.o beam.o button.o environment.o explosion.o fade.o files.o globaldata.o \
-          missile.o perlin.o physobj.o player.o satellite.o sky.o tank.o team.o teleport.o virtobj.o \
-          update.o network.o floattext.o land.o text.o client.o gameloop.o
-
-CXX?=clang++
-LIB=ar
-WINDRES=
-# FLAGS += -DDATA_DIR=\".\" -Wno-write-strings -DNETWORK -DTHREADS
-FLAGS += -DDATA_DIR=\"${INSTALLDIR}\" -DNEW_GAMELOOP -Wno-write-strings -DNETWORK -DTHREADS -pthread -Wextra -pedantic -ggdb3
-OUTPUT = ../atanks
-WFLAGS = 
-OFLAGS = 
-LFLAGS +=
-LDFLAGS +=  `allegro-config --libs` -lm -lpthread
-
-CXXFLAGS += -Wall -Iinclude # -fprofile-arcs -ftest-coverage 
-
-SRCS  = $(MODULES:.o=.cpp)
-GLOBALS	= main.h imagedefs.h externs.h
-
-all: 	$(OUTPUT)
-
-clean:  
-	rm -f *.o
-
-veryclean: clean
-	rm $(OUTPUT)
-
-$(MODULES): Makefile
-
-atanks.o: atanks.cpp globals.h main.h menucontent.h
-	$(CXX) -c atanks.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-button.o: button.cpp button.h
-	$(CXX) -c button.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-client.o: client.h client.cpp
-	$(CXX) -c client.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-environment.o: environment.cpp environment.h
-	$(CXX) -c environment.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-explosion.o: explosion.cpp explosion.h
-	$(CXX) -c explosion.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-files.o: files.cpp files.h text.h text.cpp
-	$(CXX) -c files.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-floattext.o: floattext.cpp floattext.h
-	$(CXX) -c floattext.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-gameloop.o: gameloop.cpp atanks.cpp main.h
-	$(CXX) -c gameloop.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-globaldata.o: globaldata.cpp globaldata.h
-	$(CXX) -c globaldata.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-land.o: land.cpp land.h globaldata.h environment.h
-	$(CXX) -c land.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-missile.o: missile.cpp missile.h
-	$(CXX) -c missile.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-teleport.o: teleport.cpp teleport.h
-	$(CXX) -c teleport.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-physobj.o: physobj.cpp physobj.h
-	$(CXX) -c physobj.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-player.o: player.cpp player.h
-	$(CXX) -c player.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-tank.o: tank.cpp tank.h
-	$(CXX) -c tank.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-team.o: team.cpp team.h
-	$(CXX) -c team.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-virtobj.o: virtobj.cpp virtobj.h
-	$(CXX) -c virtobj.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-fade.o: fade.cpp
-	$(CXX) -c fade.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-perlin.o: perlin.cpp
-	$(CXX) -c perlin.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-sky.o: sky.cpp sky.h
-	$(CXX) -c sky.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-satellite.o: satellite.cpp satellite.h
-	$(CXX) -c satellite.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-text.o: text.cpp text.h
-	$(CXX) -c text.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-update.o: update.cpp update.h
-	$(CXX) -c update.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-network.o: network.cpp network.h
-	$(CXX) -c network.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-%.o: %.cpp %.h
-	$(CXX) -c $< -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CXXFLAGS)
-
-$(OUTPUT): $(OBJECTS) $(MODULES)
-	$(CXX) $(MODULES) -o $(OUTPUT) $(FLAGS) $(LFLAGS) $(LDFLAGS) $(SFLAGS) $(CXXFLAGS)
-	# strip $(OUTPUT)
-
-# dependencies:
-physobj.h: main.h virtobj.h globaldata.h
-virtobj.h: main.h player.h
-main.h: imagedefs.h externs.h
-globaldata.h: main.h
-player.h: main.h menu.h
-tank.h: physobj.h
-floattext.h: virtobj.h main.h environment.h
-menu.h: globaldata.h
-environment.h: main.h tank.h
-files.h: globaldata.h environment.h 
-globals.h: virtobj.h floattext.h physobj.h tank.h missile.h explosion.h player.h environment.h globaldata.h teleport.h decor.h beam.h
-button.h: globaldata.h environment.h
-team.h: globaldata.h
-satellite.h: environment.h globaldata.h virtobj.h
-beam.h: main.h virtobj.h physobj.h
-missile.h: main.h physobj.h
-teleport.h: main.h virtobj.h
-decor.h: main.h physobj.h environment.h globaldata.h
-explosion.h: main.h physobj.h
-virtobj.cpp: virtobj.h environment.h
-physobj.cpp: physobj.h environment.h
-atanks.cpp: globals.h menu.h button.h team.h files.h satellite.h menucontent.h
-beam.cpp: environment.h globaldata.h physobj.h player.h decor.h tank.h beam.h
-button.cpp: button.h
-environment.cpp: environment.h globaldata.h virtobj.h missile.h tank.h files.h
-explosion.cpp: environment.h globaldata.h explosion.h missile.h decor.h tank.h player.h
-fade.cpp: globaldata.h main.h
-files.cpp: player.h files.h main.h
-globaldata.cpp: player.h globaldata.h files.h
-missile.cpp: environment.h globaldata.h explosion.h missile.h decor.h tank.h
-perlin.cpp: main.h
-player.cpp: environment.h globaldata.h player.h tank.h menu.h files.h floattext.h
-satellite.cpp: environment.h satellite.h beam.h
-sky.cpp: globaldata.h main.h sky.h
-tank.cpp: environment.h globaldata.h floattext.h explosion.h teleport.h missile.h player.h beam.h tank.h
-team.cpp: tank.h team.h player.h
-teleport.cpp: environment.h globaldata.h teleport.h
diff --git a/src/Makefile.bsd b/src/Makefile.bsd
deleted file mode 100644
index e07dc3c..0000000
--- a/src/Makefile.bsd
+++ /dev/null
@@ -1,151 +0,0 @@
-.PHONY: all clean veryclean
-
-MODULES = atanks.o beam.o button.o environment.o explosion.o fade.o files.o globaldata.o \
-          missile.o perlin.o physobj.o player.o satellite.o sky.o tank.o team.o teleport.o virtobj.o \
-          update.o network.o floattext.o land.o text.o client.o gameloop.o
-
-CPP=clang++
-CC=clang
-LD=clang++
-LIB=ar
-WINDRES=
-# FLAGS += -DDATA_DIR=\".\" -Wno-write-strings -DNETWORK -DTHREADS
-FLAGS += -DDATA_DIR=\"${INSTALLDIR}\" -DNEW_GAMELOOP -Wno-write-strings -DTHREADS
-OUTPUT = ../atanks
-WFLAGS = 
-OFLAGS = 
-LFLAGS +=
-LDFLAGS =  `allegro-config --libs`
-
-CFLAGS += -g -Wall -Iinclude -I/usr/local/include 
-
-SRCS  = $(MODULES:.o=.cpp)
-GLOBALS	= main.h imagedefs.h externs.h
-
-all: 	$(OUTPUT)
-
-clean:  
-	rm -f *.o
-
-veryclean: clean
-	rm $(OUTPUT)
-
-$(MODULES): Makefile
-
-atanks.o: atanks.cpp globals.h main.h menucontent.h
-	$(CPP) -c atanks.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-button.o: button.cpp button.h
-	$(CPP) -c button.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-client.o: client.h client.cpp
-	$(CPP) -c client.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-environment.o: environment.cpp environment.h
-	$(CPP) -c environment.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-explosion.o: explosion.cpp explosion.h
-	$(CPP) -c explosion.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-files.o: files.cpp files.h text.h text.cpp
-	$(CPP) -c files.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-floattext.o: floattext.cpp floattext.h
-	$(CPP) -c floattext.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-gameloop.o: gameloop.cpp atanks.cpp main.h
-	$(CPP) -c gameloop.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-globaldata.o: globaldata.cpp globaldata.h
-	$(CPP) -c globaldata.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-land.o: land.cpp land.h globaldata.h environment.h
-	$(CPP) -c land.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-missile.o: missile.cpp missile.h
-	$(CPP) -c missile.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-teleport.o: teleport.cpp teleport.h
-	$(CPP) -c teleport.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-physobj.o: physobj.cpp physobj.h
-	$(CPP) -c physobj.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-player.o: player.cpp player.h
-	$(CPP) -c player.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-tank.o: tank.cpp tank.h
-	$(CPP) -c tank.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-team.o: team.cpp team.h
-	$(CPP) -c team.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-virtobj.o: virtobj.cpp virtobj.h
-	$(CPP) -c virtobj.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-fade.o: fade.cpp
-	$(CPP) -c fade.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-perlin.o: perlin.cpp
-	$(CPP) -c perlin.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-sky.o: sky.cpp sky.h
-	$(CPP) -c sky.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-satellite.o: satellite.cpp satellite.h
-	$(CPP) -c satellite.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-text.o: text.cpp text.h
-	$(CPP) -c text.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-update.o: update.cpp update.h
-	$(CPP) -c update.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-network.o: network.cpp network.h
-	$(CPP) -c network.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-%.o: %.cpp %.h
-	$(CPP) -c $< -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-$(OUTPUT): $(OBJECTS) $(MODULES)
-	$(CPP) $(MODULES) -o $(OUTPUT) $(FLAGS) $(LFLAGS) $(LDFLAGS) $(SFLAGS) $(CFLAGS)
-	# strip $(OUTPUT)
-
-# dependencies:
-physobj.h: main.h virtobj.h globaldata.h
-virtobj.h: main.h player.h
-main.h: imagedefs.h externs.h
-globaldata.h: main.h
-player.h: main.h menu.h
-tank.h: physobj.h
-floattext.h: virtobj.h main.h environment.h
-menu.h: globaldata.h
-environment.h: main.h tank.h
-files.h: globaldata.h environment.h 
-globals.h: virtobj.h floattext.h physobj.h tank.h missile.h explosion.h player.h environment.h globaldata.h teleport.h decor.h beam.h
-button.h: globaldata.h environment.h
-team.h: globaldata.h
-satellite.h: environment.h globaldata.h virtobj.h
-beam.h: main.h virtobj.h physobj.h
-missile.h: main.h physobj.h
-teleport.h: main.h virtobj.h
-decor.h: main.h physobj.h environment.h globaldata.h
-explosion.h: main.h physobj.h
-virtobj.cpp: virtobj.h environment.h
-physobj.cpp: physobj.h environment.h
-atanks.cpp: globals.h menu.h button.h team.h files.h satellite.h menucontent.h
-beam.cpp: environment.h globaldata.h physobj.h player.h decor.h tank.h beam.h
-button.cpp: button.h
-environment.cpp: environment.h globaldata.h virtobj.h missile.h tank.h files.h
-explosion.cpp: environment.h globaldata.h explosion.h missile.h decor.h tank.h player.h
-fade.cpp: globaldata.h main.h
-files.cpp: player.h files.h main.h
-globaldata.cpp: player.h globaldata.h files.h
-missile.cpp: environment.h globaldata.h explosion.h missile.h decor.h tank.h
-perlin.cpp: main.h
-player.cpp: environment.h globaldata.h player.h tank.h menu.h files.h floattext.h
-satellite.cpp: environment.h satellite.h beam.h
-sky.cpp: globaldata.h main.h sky.h
-tank.cpp: environment.h globaldata.h floattext.h explosion.h teleport.h missile.h player.h beam.h tank.h
-team.cpp: tank.h team.h player.h
-teleport.cpp: environment.h globaldata.h teleport.h
diff --git a/src/Makefile.windows b/src/Makefile.windows
deleted file mode 100644
index 3aaf235..0000000
--- a/src/Makefile.windows
+++ /dev/null
@@ -1,157 +0,0 @@
-.PHONY: all clean veryclean
-
-MODULES = atanks.o beam.o button.o environment.o explosion.o fade.o files.o globaldata.o \
-          missile.o perlin.o physobj.o player.o satellite.o sky.o tank.o team.o teleport.o virtobj.o update.o network.o \
-          floattext.o land.o text.o client.o gameloop.o
-MODULES += atanks.res
-
-# CPP=g++
-CPP=mingw32-g++
-CC=gcc
-LD=g++
-LIB=ar
-WINDRES=windres.exe
-FLAGS += -DNEW_GAMELOOP -DDATA_DIR=\".\" -Wno-write-strings
-#FLAGS += -DDATA_DIR=\"${INSTALLDIR}\"  -Wno-write-strings -DTHREADS
-OUTPUT = ../atanks.exe
-WFLAGS = 
-OFLAGS = 
-LFLAGS += -mwindows
-LDFLAGS = -L../.. -lalleg
-#LDFLAGS += -lpthread
-
-CFLAGS += -Wall -Iinclude # -fprofile-arcs -ftest-coverage 
-
-SRCS  = $(MODULES:.o=.cpp)
-GLOBALS    = main.h imagedefs.h externs.h
-
-all:     $(OUTPUT)
-
-clean:  
-	rm -f *.o
-
-veryclean: clean
-	rm $(OUTPUT)
-
-$(MODULES): Makefile
-
-atanks.res:
-	$(WINDRES) -i atanks.rc --input-format=rc -o atanks.res -O coff
-
-atanks.o: atanks.cpp globals.h main.h menucontent.h
-	$(CPP) -c atanks.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-button.o: button.cpp button.h
-	$(CPP) -c button.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-client.o: client.h client.cpp
-	$(CPP) -c client.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-environment.o: environment.cpp environment.h
-	$(CPP) -c environment.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-explosion.o: explosion.cpp explosion.h
-	$(CPP) -c explosion.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-files.o: files.cpp files.h text.h text.cpp
-	$(CPP) -c files.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-floattext.o: floattext.cpp floattext.h
-	$(CPP) -c floattext.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-gameloop.o: gameloop.h gameloop.cpp
-	$(CPP) -c gameloop.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-globaldata.o: globaldata.cpp globaldata.h
-	$(CPP) -c globaldata.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-land.o: land.cpp land.h globaldata.h environment.h
-	$(CPP) -c land.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-missile.o: missile.cpp missile.h
-	$(CPP) -c missile.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-teleport.o: teleport.cpp teleport.h
-	$(CPP) -c teleport.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-physobj.o: physobj.cpp physobj.h
-	$(CPP) -c physobj.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-player.o: player.cpp player.h
-	$(CPP) -c player.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-tank.o: tank.cpp tank.h
-	$(CPP) -c tank.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-team.o: team.cpp team.h
-	$(CPP) -c team.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-virtobj.o: virtobj.cpp virtobj.h
-	$(CPP) -c virtobj.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-fade.o: fade.cpp
-	$(CPP) -c fade.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-perlin.o: perlin.cpp
-	$(CPP) -c perlin.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-sky.o: sky.cpp sky.h
-	$(CPP) -c sky.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-satellite.o: satellite.cpp satellite.h
-	$(CPP) -c satellite.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-text.o: text.cpp text.h
-	$(CPP) -c text.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-update.o: update.cpp update.h
-	$(CPP) -c update.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-network.o: network.cpp network.h
-	$(CPP) -c network.cpp -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-%.o: %.cpp %.h
-	$(CPP) -c $< -o $@ $(FLAGS) $(LFLAGS) $(OFLAGS) $(CFLAGS)
-
-$(OUTPUT): $(OBJECTS) $(MODULES)
-	$(CPP) $(MODULES) -o $(OUTPUT) $(FLAGS) $(LFLAGS) $(LDFLAGS) $(SFLAGS) $(CFLAGS)
-
-# dependencies:
-physobj.h: main.h virtobj.h globaldata.h
-virtobj.h: main.h player.h
-main.h: imagedefs.h externs.h
-globaldata.h: main.h
-player.h: main.h menu.h
-tank.h: physobj.h
-floattext.h: virtobj.h main.h environment.h
-menu.h: globaldata.h
-environment.h: main.h tank.h
-files.h: globaldata.h environment.h
-globals.h: virtobj.h floattext.h physobj.h tank.h missile.h explosion.h player.h environment.h globaldata.h teleport.h decor.h beam.h
-button.h: globaldata.h environment.h
-team.h: globaldata.h
-satellite.h: environment.h globaldata.h virtobj.h
-beam.h: main.h virtobj.h physobj.h
-missile.h: main.h physobj.h
-teleport.h: main.h virtobj.h
-decor.h: main.h physobj.h environment.h globaldata.h
-explosion.h: main.h physobj.h
-virtobj.cpp: virtobj.h environment.h
-physobj.cpp: physobj.h environment.h
-atanks.cpp: globals.h menu.h button.h team.h files.h satellite.h menucontent.h
-beam.cpp: environment.h globaldata.h physobj.h player.h decor.h tank.h beam.h
-button.cpp: button.h
-environment.cpp: environment.h globaldata.h virtobj.h missile.h tank.h files.h
-explosion.cpp: environment.h globaldata.h explosion.h missile.h decor.h tank.h player.h
-fade.cpp: globaldata.h main.h
-files.cpp: player.h files.h main.h
-globaldata.cpp: player.h globaldata.h files.h
-missile.cpp: environment.h globaldata.h explosion.h missile.h decor.h tank.h
-perlin.cpp: main.h
-player.cpp: environment.h globaldata.h player.h tank.h menu.h files.h floattext.h
-satellite.cpp: environment.h satellite.h beam.h
-sky.cpp: globaldata.h main.h sky.h
-tank.cpp: environment.h globaldata.h floattext.h explosion.h teleport.h missile.h player.h beam.h tank.h
-team.cpp: tank.h team.h player.h
-teleport.cpp: environment.h globaldata.h teleport.h
-
diff --git a/src/aicore.cpp b/src/aicore.cpp
new file mode 100644
index 0000000..3746d8c
--- /dev/null
+++ b/src/aicore.cpp
@@ -0,0 +1,5584 @@
+#include "aicore.h"
+#include "player.h"
+#include "tank.h"
+#include "missile.h"
+#include "beam.h"
+#include "explosion.h"
+
+#include <cassert>
+
+
+/// @brief Maximum AI Level is the highest level being lucky, thus +1.
+const int32_t maxAiLevel = DEADLY_PLAYER + 1;
+
+
+/** @struct sItemListEntry
+  * @brief doubly linked list element to organize the AIs item preferences.
+**/
+struct sItemListEntry
+{
+	int32_t         amount     = 0;       //!< Number of items in stock.
+	bool            escape     = false;   //!< Set to true if "getting away" points are awarded.
+	bool            kamikaze   = false;   //!< Set to true if self destruct points are awarded.
+	sItemListEntry* next       = nullptr;
+	int32_t         preference = 0;       //!< Shortcut to the players preferences.
+	sItemListEntry* prev       = nullptr;
+	int32_t         score      = 0;       //!< How likely the AI uses this item.
+	bool            selectable = false;   //!< Some are not selectable, like parachutes.
+	int32_t         type       = 0;       //!< The (enum) itemType of the item
+
+	explicit sItemListEntry(sItemListEntry* prev_);
+	~sItemListEntry();
+
+	const char* getName() { return item[type].getName(); }
+};
+
+
+/** @struct sOppMemEntry
+  * @brief doubly linked list element to organize the AIs opponent memory.
+**/
+struct sOppMemEntry
+{
+	bool          alive       = true;    //!< False if the tank is destroyed.
+	int32_t       attempts    = 0;       //!< How often tried to hit this round.
+	int32_t       buried_l    = 0;       //!< Buried level to the left.
+	int32_t       buried_r    = 0;       //!< Buried level to the right.
+	double        diffLife    = 0.;      //!< Difference to bots life value: (this - opp).
+	double        distance    = 0.;      //!< Shortcut to the absolute distance between both tanks.
+	int32_t       dmgDone     = 0;       //!< damage done in simulation to calculate hit score.
+	sOpponent*    entry       = nullptr; //!< The AIs sOpponent memory (see players.h).
+	bool          hasRepulse  = false;   //!< Whether or not the opponent has a repulsor shield up.
+	sOppMemEntry* next        = nullptr;
+	bool          is_buried   = 0;       //!< Whether buried_l+buried_r is greater than BURIED_LEVEL.
+	bool          onSameTeam  = false;   //!< True if on the same team as the player.
+	double        opLife      = 0.;      //!< Full opponents life, which is tank->sh + tank->l.
+	double        opX         = 0;       //!< X-coordinate of the opponents tank.
+	double        opY         = 0;       //!< Y-coordinate of the opponents tank.
+	sOppMemEntry* prev        = nullptr;
+	bool          revengeDone = false;   //!< Wether the score has already taken revenge into account.
+	int32_t       score       = 0;       //!< How likely the AI attacks this opponent.
+	double        team_mod    = 1.;      //!< Multiplier for the score according to which teams both belong to.
+
+	explicit sOppMemEntry(sOppMemEntry* prev_);
+	~sOppMemEntry();
+
+	const char* getName() { return entry->opponent->getName(); }
+};
+
+
+/** @struct sWeapListEntry
+  * @brief doubly linked list element to organize the AIs weapon preferences
+**/
+struct sWeapListEntry
+{
+	int32_t         amount     = 0;     //!< Number of weapons in stock.
+	bool            blastOut   = false; //!< Set to true if blasting out points are awarded.
+	double          dmgCluster = 0.;    //!< Cluster full damage.
+	double          dmgSingle  = 0.;    //!< Single shot damage.
+	double          dmgSpread  = 0.;    //!< Spread full damage.
+	bool            kamikaze   = false; //!< Set to true if self destruct points are awarded.
+	sWeapListEntry* next       = nullptr;
+	int32_t         preference = 0;     //!< Shortcut to the players preferences.
+	sWeapListEntry* prev       = nullptr;
+	int32_t         radius     = 0;     //!< Blast radius of the weapon.
+	int32_t         score      = 0;     //!< How likely the AI uses this weapon.
+	int32_t         spread     = 1;     //!< Checked weapon spread value. (See AICore::getMemory())
+	int32_t         subMunCount= 0;     //!< Number of sub munition "bomblets"
+	int32_t         subMunType = -1;    //!< Clusters and such have sub munition.
+	int32_t         type       = 0;     //!< The (enum) weaponType of the weapon.
+
+	explicit sWeapListEntry(sWeapListEntry* prev_);
+	~sWeapListEntry();
+
+	const char* getName() { return weapon[type].getName(); }
+};
+
+
+/// @brief Template swapper, the types just need prev/next pointers
+template<typename T> void swap_entries(T* lhs, T* rhs)
+{
+	if (lhs && rhs && (lhs != rhs)) {
+		// backup neighbourhood (and use as short cuts ;-) )
+		T* l_next = lhs->next;
+		T* l_prev = lhs->prev;
+		T* r_next = rhs->next;
+		T* r_prev = rhs->prev;
+
+		// Insert rhs into lhs location
+		if (l_next && (l_next != rhs)) l_next->prev = rhs;
+		if (l_prev && (l_prev != rhs)) l_prev->next = rhs;
+
+		// Insert lhs into rhs location
+		if (r_next && (r_next != lhs)) r_next->prev = lhs;
+		if (r_prev && (r_prev != lhs)) r_prev->next = lhs;
+
+		// Move rhs to lhs location
+		rhs->next = l_next == rhs ? lhs : l_next;
+		rhs->prev = l_prev == rhs ? lhs : l_prev;
+
+		// Move lhs to (former) rhs location
+		lhs->next = r_next == lhs ? rhs : r_next;
+		lhs->prev = r_prev == lhs ? rhs : r_prev;
+	}
+}
+
+
+/// @brief Template sorter, the types need prev, next and score.
+/// Sorting is done by score in descending order. If *head is sorted
+/// down the list, it is set to the new first element.
+template<typename T> void sort_entries(T** head)
+{
+	if (!head || !(*head) )
+		return;
+
+	bool sorted = false;
+
+	while (!sorted) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		sorted = true;
+
+		T* curr = *head;
+		T* next = curr->next;
+
+		while (next) {
+			if (next->score > curr->score) {
+				sorted = false;
+				swap_entries(curr, next);
+				if (*head == curr)
+					*head = next;
+			} else
+				curr = next;
+			next = curr->next;
+		} // End of having next
+	} // end of not sorted
+
+#if defined(ATANKS_DEBUG_AIMING) || defined(ATANKS_DEBUG_EMOTIONS)
+	DEBUG_LOG_AI("Memory Sorting", "Sorting results:", 0)
+	T* curr = *head;
+	int32_t nr = 1;
+	while (curr) {
+		if (curr->score > -10000) {
+			DEBUG_LOG_AI("Memory Sorting", "% 3d: Score %5d (%s)",
+						 nr++, curr->score, curr->getName())
+			curr = curr->next;
+		} else
+			curr = nullptr;
+	}
+#endif // ATANKS_DEBUG_AIMING || ATANKS_DEBUG_EMOTIONS
+}
+
+
+/// @brief AICore default constructor
+AICore::AICore() :
+	textAllowed(ATOMIC_VAR_INIT(true))
+{
+	// As the opponent counts, and both weapons and items
+	// list sizes are fixed, memory is reserved here.
+
+	/// 1) Items
+    for (int32_t i = 0; canWork && (i < ITEMS); ++i) {
+		try {
+			item_curr = new itEntry_t(item_last);
+			if (!item_head)
+				item_head = item_curr;
+			item_last = item_curr;
+		} catch (std::exception &e) {
+			cerr << "Unable to reserve " << sizeof(itEntry_t);
+			cerr << " bytes for AI item chain: " << e.what() << endl;
+
+			destroy();
+			canWork = false;
+		}
+    }
+
+	/// 2) Opponents
+    for (int32_t i = 0; canWork && (i < env.numGamePlayers); ++i) {
+		try {
+			mem_curr = new opEntry_t(mem_last);
+			if (!mem_head)
+				mem_head = mem_curr;
+			mem_last = mem_curr;
+		} catch (std::exception &e) {
+			cerr << "Unable to reserve " << sizeof(opEntry_t);
+			cerr << " bytes for AI memory chain: " << e.what() << endl;
+
+			destroy();
+			canWork = false;
+		}
+    }
+
+	/// 3) Weapons
+    for (int32_t i = 0; canWork && (i < WEAPONS); ++i) {
+		try {
+			weap_curr = new weEntry_t(weap_last);
+			if (!weap_head)
+				weap_head = weap_curr;
+			weap_last = weap_curr;
+		} catch (std::exception &e) {
+			cerr << "Unable to reserve " << sizeof(weEntry_t);
+			cerr << " bytes for AI weapon chain: " << e.what() << endl;
+
+			destroy();
+			canWork = false;
+		}
+    }
+
+    // Stop if no work can be done
+    isStopped = !canWork;
+
+    DEBUG_LOG_AI("AICore", "Instance created", 0)
+}
+
+
+/// @brief AICore destructor
+AICore::~AICore()
+{
+	if (isWorking) {
+		if (!isStopped)
+			this->stop();
+		while (isWorking)
+			std::this_thread::yield();
+	}
+
+	// Clean up memory chains:
+	this->destroy();
+
+	DEBUG_LOG_AI("AICore", "Instance destroyed", 0)
+}
+
+
+/// @brief return the currently active player or nullptr if not working
+PLAYER* AICore::active_player() const
+{
+	if (isWorking)
+		return player;
+	return nullptr;
+}
+
+
+/** @brief aim the current selection
+  * @param[in] is_last if set to true, the best result is accepted, no matter
+  * what the outcome might be.
+  * @return true if the aiming resulted in a usable hit.
+**/
+bool AICore::aim(bool is_last)
+{
+	plStage = PS_AIM;
+
+	DEBUG_LOG_AIM(player->getName(), "Starting to aim %s at %s",
+				weapon[weap_idx].getName(), mem_curr->entry->opponent->getName())
+
+	int32_t attempt  = 0;
+
+	// reset current values as there can be no guarantee that the
+	// last selected combination works for the current weapon/opponent
+	// selection.
+	sanitizeCurr();
+	hill_detected    = false;
+	// Note: curr_overshoot is reset to MAX_OVERSHOOT in calcAttack() but
+	// might have an actual traced value from calcBoxed(), so do not reset
+	// it here again.
+
+
+	// Reset aiming round memory
+	best_score      = NEUTRAL_ROUND_SCORE;
+	best_angle      = angle;
+	best_power      = power;
+	best_prime_hit  = false;
+	best_overshoot  = MAX_OVERSHOOT;
+	last_ang_mod    = 0;
+	last_overshoot  = MAX_OVERSHOOT;
+	last_pow_mod    = 0;
+	last_reverted   = false;
+	last_score      = 0;
+	last_was_better = false;
+	reached_x       = x;
+	reached_y       = y;
+
+
+	// loop until finished, forced off or ending unsuccessfully
+	while (!isStopped && (++attempt <= findRngAttempts) ) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		int32_t hit_score      = 0;
+		int32_t has_crashed    = 0;
+		int32_t has_finished   = 0;
+
+		// Modifications for this round:
+		int32_t ang_mod =   1 + RAND_AI_1P; // [ 1;  7]
+		int32_t pow_mod = (10 + RAND_AI_1P) // [10; 17]
+						* curr_power / 100; // [10;340]
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "[%d/%d] Angle % 3d, Power % 4d",
+		              attempt, findRngAttempts,
+		              GET_DISP_ANGLE(curr_angle), curr_power)
+
+		// See where we are going:
+		traceWeapon(has_crashed, has_finished);
+		hit_score = calcHitScore(is_last && needSuccess);
+
+
+		// See whether this shot actually got nearer to the opponent.
+		// (Note: Otherwise a better score just means less collateral damage!)
+		bool is_nearer = std::abs(last_overshoot) >= std::abs(curr_overshoot);
+
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "[%d/%d] => Score %d, Overshoot %d (%s [last: %d])",
+		              attempt, findRngAttempts, hit_score, curr_overshoot,
+		              is_nearer ? "Nearer" : "Farther", last_overshoot)
+
+
+		// Note down a new best score:
+		bool new_best_score = (hit_score > best_score);
+		if ( ( new_best_score && curr_prime_hit)
+		  || (!best_prime_hit && (new_best_score || curr_prime_hit) ) ) {
+			// Note: Better score with prime hit, prime hit for the first time,
+			//       or better score with prime never hit.
+			DEBUG_LOG_AIM(player->getName(),
+						  "[%d/%d] => New best score %d [%d best]",
+						  attempt, findRngAttempts,
+						  hit_score, best_score)
+
+			sanitizeCurr();
+
+			best_angle     = curr_angle;
+			best_overshoot = curr_overshoot;
+			best_power     = curr_power;
+			best_prime_hit = curr_prime_hit;
+			best_score     = hit_score;
+
+			// The last modifications seem to have brought something
+			ang_mod = (last_ang_mod + (SIGN(last_ang_mod) * ang_mod)) / 2;
+			pow_mod = (last_pow_mod + pow_mod) / 2 * SIGN(best_overshoot);
+		}
+
+
+		// The outcome has to be checked:
+		if (canWork && !isStopped) {
+
+			/* The following situations can occur:
+			 *
+			 * A) The shot (or part of them) have not been finished.
+			 *    In this case power must be reduced and the angle
+			 *    has to be moved in a more neutral position if it
+			 *    is too steep or flat.
+			 * B) With steel or wrap wall the shot might crash into
+			 *    a wrap ceiling or the wall and ceiling made of steel.
+			 *    Generally this can only fixed by lowering power and
+			 *    getting away from 45° angles.
+			 * C) The hit is nearer than the last one.
+			 *    This is good. If it even has a positive hit_score,
+			 *    it can be noted down as a new best hit.
+			 * D) The hit is farther away. If the score is better,
+			 *    the direction of modification seems correct.
+			 *    Otherwise it might be better to revert those changes.
+			 */
+
+			// --- Situation A) The shot was not finished. ---
+			//-------------------------------------------------
+			if ( !has_finished // Pure situation A must be taken care of
+			  || ( (has_finished < weap_curr->spread)
+				&& ( (has_finished - RAND_AI_0P) < 0) ) ) {
+
+				DEBUG_LOG_AIM(player->getName(),
+								"[%d/%d] %d / %d finished, trying to correct",
+								attempt, findRngAttempts,
+								has_finished, weap_curr->spread)
+
+				fixUnfinished(ang_mod, pow_mod);
+				last_reverted   = (SIGN(last_ang_mod) != SIGN(ang_mod));
+				last_was_better = false;
+			} // End of having lost the shot prediction
+
+
+			// --- Situation B) The shot(s) crashed       ---
+			//------------------------------------------------
+			else if ( (has_crashed == weap_curr->spread)
+			       || ( (has_crashed > 0)
+			         && ((has_crashed + RAND_AI_0P) >= weap_curr->spread)) ) {
+
+				DEBUG_LOG_AIM(player->getName(),
+								"[%d/%d] %d / %d crashed, trying to correct",
+								attempt, findRngAttempts,
+								has_crashed, weap_curr->spread)
+
+				fixCrashed(ang_mod, pow_mod);
+
+				// If there is a positive hit_score, halve it for every
+				// shot that crashed:
+				if (hit_score > 0) {
+					for (int32_t i = 0; i < has_crashed; ++i) {
+						if (RAND_AI_0P)
+							hit_score /= 2;
+					}
+				}
+
+				last_reverted   = (SIGN(last_ang_mod) != SIGN(ang_mod));
+				last_was_better = false;
+			}
+
+
+			// --- Situation C) The shot is nearer to the target. ---
+			//--------------------------------------------------------
+			else if (is_nearer) {
+				// Note: if this is a new best score, some adaptation has
+				//       already been made above.
+				if (hit_score < last_score) {
+					DEBUG_LOG_AIM(player->getName(),
+					              "[%d/%d] => Nearer but not a better score %d [%d best]",
+					              attempt, findRngAttempts,
+					              hit_score, best_score)
+
+					// Just modify the new angle mod to mimic the last
+					// with new values
+					ang_mod = std::abs(ang_mod) * SIGN(last_ang_mod);
+
+					last_was_better = true;
+				} else
+					last_was_better = false;
+
+				last_reverted   = (SIGN(last_ang_mod) != SIGN(ang_mod));
+
+				// pow_mod must have the opposite sign of the overshoot:
+				pow_mod = std::abs(pow_mod) * SIGN(curr_overshoot) * -1;
+
+				DEBUG_LOG_AIM(player->getName(),
+							"[%d/%d] New angle mod %d, new power mod %d",
+							attempt, findRngAttempts,
+							ang_mod, pow_mod)
+			} // End of having a nearer hit
+
+
+			// --- Situation D) The shot hit farther away than the best. ---
+			//---------------------------------------------------------------
+			else {
+				DEBUG_LOG_AIM(player->getName(),
+				              "[%d/%d] Farther impact (%d curr, %d best)"
+				              " [score %d]",
+				              attempt, findRngAttempts, curr_overshoot,
+				              best_overshoot, hit_score)
+
+				fixOvershoot(ang_mod, pow_mod, hit_score);
+
+				last_reverted = SIGN(ang_mod) != SIGN(last_ang_mod);
+
+				DEBUG_LOG_AIM(player->getName(),
+							"[%d/%d] New angle mod %d, new power mod %d",
+							attempt, findRngAttempts,
+							ang_mod, pow_mod)
+			} // End of situation C
+
+
+			// Try to fix 180° shots if no positive score was achieved:
+			if ( (180 == curr_angle) && !ang_mod && (hit_score < 1) ) {
+				ang_mod = SIGN(mem_curr->opX - x)
+						* (RAND_AI_1P + 1)
+						* -1.;
+				DEBUG_LOG_AIM(player->getName(),
+							  "Vertical shot detected, new angle mod %d",
+							  ang_mod)
+			}
+
+
+			// Power modification can be modified by a difference between
+			// the overshoot and the actual modification according to
+			// AI settings:
+			// ----------------------------------------------------------
+			double power_diff = (std::abs(curr_overshoot) - std::abs(pow_mod))
+			                  / (std::abs(ang_mod) ? std::abs(ang_mod) : 1);
+
+			if ( (curr_overshoot < MAX_OVERSHOOT)
+			  && (power_diff > std::abs(pow_mod))
+			  && (hit_score < 1) ) {
+				DEBUG_LOG_AIM(player->getName(),
+				              "Too low power mod difference %d"
+				              " (overshoot %d, pow_mod %d)",
+				              ROUND(power_diff), curr_overshoot, ROUND(pow_mod))
+
+				pow_mod += power_diff * focusRate / 2. * SIGNd(pow_mod);
+
+				DEBUG_LOG_AIM(player->getName(),
+				              "Hopefully fixed power mod: %d",
+				              pow_mod)
+			}
+
+
+			// Make sure both modifications applied end in a sane results:
+			// -----------------------------------------------------------
+
+			// Test angle to the right
+			if ( (curr_angle + ang_mod) < 90) {
+				if (curr_angle > 90)
+					// Just sanitize
+					ang_mod = 90 - curr_angle;
+				else
+					// Pull up to try again from a very different view
+					ang_mod = ROUNDu(60. * focusRate) // + [10;60]
+					        - (RAND_AI_0P * 5);       // - [ 5;25]
+			} // End of sanitizing angle right
+
+			// Test angle to the left
+			else if ( (curr_angle + ang_mod) > 270) {
+				if (curr_angle < 270)
+					// Just sanitize
+					ang_mod = 270 - curr_angle;
+				else
+					// Pull up to try again from a very different view
+					ang_mod = (-60. * focusRate) // - [10;60]
+					        + (RAND_AI_0P * 5);  // + [ 0;25]
+			} // End of sanitizing angle left
+
+
+			// Test bottom power range
+			if ( (curr_power + pow_mod) < MIN_POWER) {
+				if (curr_power > MIN_POWER)
+					// Just sanitize
+					pow_mod = MIN_POWER - curr_power;
+				else
+					// Give more power to go somewhere else
+					pow_mod = (900. * focusRate) // + [150;900]
+					        - (RAND_AI_0P * 50); // - [  0;250]
+			}
+
+			// Test upper power range
+			if ( (curr_power + pow_mod) > MAX_POWER) {
+				if (curr_power < MAX_POWER)
+					// Just sanitize
+					pow_mod = MAX_POWER - curr_power;
+				else
+					// Give more power to go somewhere else
+					pow_mod = (-900. * focusRate) // - [150;900]
+					        + (RAND_AI_0P * 50);  // + [  0;250]
+			}
+
+
+			// Apply mods:
+			curr_angle   += ang_mod;
+			curr_power   += pow_mod;
+
+
+			// Save current score, mods and overshot:
+			last_ang_mod   = ang_mod;
+			last_overshoot = curr_overshoot;
+			last_pow_mod   = pow_mod;
+			last_score     = hit_score;
+		} // End of canWork and not isStopped
+
+
+	} // end of aiming loop
+
+	DEBUG_LOG_AIM(player->getName(),
+				"Final score with angle %d, power %d : %d => %s%s",
+				GET_DISP_ANGLE(best_angle), best_power, best_score,
+				(best_score > 0) || (is_last && needSuccess) ? "Success!" : "Failure!",
+				is_last && needSuccess ? " (is_last forced!)" : "")
+
+	// If this was the last try and it did not reach the target having
+	// a negative best score, assume that the path is blocked.
+	// However, if a best setup is already known, revert to that.
+	if ( (weap_idx < WEAPONS) && is_last && needSuccess
+	  && (best_setup_score < 0)
+	  && (best_round_score < 0)
+	  && (best_score < 0)
+	  && (best_overshoot < 0) // too short
+	  && ( ( (reached_y > BOXED_TOP)                 // Not a ceiling crash,
+	      && (-best_overshoot > weap_curr->radius) ) // but can't hit
+	    || hill_detected ) /* if a hill was detected, it must be removed */ ) {
+		bool free_tank = std::abs(reached_x - x) < weapon[RIOT_BLAST].radius;
+
+		if (useFreeingTool(free_tank, is_last)) {
+			needAim       = false;
+			isBlocked     = true;
+			hill_detected = true;
+
+			if (free_tank)
+				calcUnbury(is_last);
+			else {
+				// Write back best values
+				curr_angle = best_angle;
+				curr_power = best_power;
+
+				// If this is a shot that got too short and a riot bomb
+				// is chosen, flatten the angle to hit the mountain in between
+				flattenCurrAng();
+
+				// Now set the results:
+				sanitizeCurr();
+				angle      = curr_angle;
+				power      = curr_power;
+				DEBUG_LOG_AIM(player->getName(),
+							  "Obstacle detected, trying to clear path using %s",
+							  weap_idx < WEAPONS
+							  ? weapon[weap_idx].getName()
+							  : item[weap_idx - WEAPONS].getName())
+			}
+			return true;
+		}
+	}
+
+	// Write back best values if this is a success:
+	if (best_score > best_round_score) {
+		best_round_score = best_score;
+		curr_angle       = best_angle;
+		curr_power       = best_power;
+		sanitizeCurr();
+	}
+
+
+	return ( (best_round_score > 0) || (is_last && needSuccess) );
+}
+
+
+/// @brief Allow AICore to create FLOATTEXT instances
+void AICore::allowText()
+{
+	textAllowed.store(true, ATOMIC_WRITE);
+}
+
+
+/** @brief calculate basic attack values or set up the last ones
+  * @param[in] attempt If this equals findTgtAttempts, this method is forced to
+  *            not check too harshly, so it always returns true.
+  * @return true if the method came up with something sane.
+**/
+bool AICore::calcAttack(int32_t attempt)
+{
+	bool is_last   = ((attempt == findTgtAttempts) && needSuccess);
+	plStage        = PS_CALCULATE;
+	isBlocked      = false;
+	needAim        = false;
+	curr_overshoot = MAX_OVERSHOOT;
+	offset_x       = 0;
+	offset_y       = 0;
+
+	// If an item is chosen over a weapon, nothing is to be done
+	if (item_curr && !weap_curr) {
+		// If an item is selected, write back the currently used
+		// angle and power, nothing is to be changed now.
+		curr_angle = angle;
+		curr_power = power;
+		return true;
+	}
+
+	assert(weap_curr
+		&& "ERROR: weap_curr is nullptr in calcAttack() with no item chosen!");
+
+	// If the currently chosen opponent is the one attacked
+	// in the last round, simply copy back the old attack
+	// values and be done
+	if ( last_opp
+	  && (last_opp->opponent != player) // don't repeat self destruct attempts
+	  && (last_opp  == mem_curr->entry)
+	  && (last_weap == weap_idx) ) {
+		curr_angle = last_ang;
+		curr_power = last_pow;
+
+		if (weap_idx < WEAPONS)
+			needAim = true;
+
+		return true;
+	}
+
+	/* If the current target is different or there was no last target,
+	 * a basic set of values must be generated.
+	 *
+	 * Outline:
+	 * --------
+	 * There are five possible scenarios:
+	 * a) The tank is not buried (enough) and a laser is chosen:
+	 *    -> a direct angle will do, make sure power is sane.
+	 * b) The tank is buried and an appropriate tool is chosen:
+	 *    -> fire tool at the most filled side or, if the difference is less
+	 *       than the AI level, in the direction of the chosen opponent.
+	 * c) Kamikaze
+	 *    -> indicated by setting mem_curr to the own entry
+	 *    -> if shaped weapon is chosen, fire 45° and power 150 to the side
+	 *       where the terrain height is nearest to this tanks bottom.
+	 *    -> if napalm is chosen, fire against the wind with power 100 - 300
+	 *    -> otherwise fire 180° and power 200 + spread modification.
+	 * d) Fire in non-boxed mode
+	 *    -> normal calculation
+	 * e) Fire in boxed mode
+	 *    -> extended power-control after normal calculation
+	 *    -> if the target can't be reached while staying below the ceiling,
+	 *       check for an obstacle that can be removed and do so if found.
+	 */
+
+	DEBUG_LOG_AIM(player->getName(), "[%d / %d] Starting to aim at %s",
+				attempt, findTgtAttempts, mem_curr->entry->opponent->getName())
+	DEBUG_LOG_AIM(player->getName(), "Aim from %d/%d to %d/%d [distance %d/%d]",
+				ROUND(x), ROUND(y),
+				ROUND(mem_curr->opX), ROUND(mem_curr->opY),
+				ROUND(mem_curr->opX - x), ROUND(mem_curr->opY - y))
+
+	/* Case a) The tank is not buried (enough) and a laser is chosen
+	 * ===================================================================
+	 */
+	if ( (buried < BURIED_LEVEL)
+	  && (SML_LAZER <= weap_idx)
+	  && (LRG_LAZER >= weap_idx) )
+		return calcLaser(is_last);
+
+
+	/* Case b) The tank is buried and an appropriate tool is chosen
+	 * ===================================================================
+	 * (This means that it must be checked whether this is an appropriate
+	 *  tool or not. Here the method might fail if it isn't suitable.)
+	 */
+	if (buried >= BURIED_LEVEL)
+		return calcUnbury(is_last);
+
+
+	/* Case c) Kamikaze
+	 * ===================================================================
+	 */
+	if (mem_curr->entry->opponent == player)
+		return calcKamikaze(is_last);
+
+
+	/* Case d) Fire in non-boxed mode
+	 * ===================================================================
+	 * This is always done, the boxed mode variant below simply checks the
+	 * values and tries to adapt.
+	 * The flipping is only allowed if the same opponent is tried again.
+	 */
+	bool result = calcStandard(is_last, (0 == (++mem_curr->attempts % 2)));
+
+
+	/* Case e) Fire in boxed mode
+	 * ===================================================================
+	 *    -> extended power-control after normal calculation
+	 *    -> if the target can't be reached while staying below the ceiling,
+	 *       check for an obstacle that can be removed and do so if found.
+	 */
+	if (result && env.isBoxed && !isBlocked && needAim && (weap_idx < WEAPONS))
+		result = calcBoxed(is_last);
+
+
+	return result;
+}
+
+
+/** @brief Case e) Fire in boxed mode
+  *
+  * Note: calcAttack() has to make sure this method is only called if it is
+  * appropriate. No further checks are made within this method.
+  *
+  * @param[in] is_last If this is set to true, the method is forced to succeed.
+  * @return true if sane values could be found.
+**/
+bool AICore::calcBoxed(bool is_last)
+{
+	// Return at once if the bot "forgets" that there is a ceiling:
+	if (!is_last && RAND_AI_1N)
+		// With this even the useless bot has only a ~33% chance to forget...
+		return true;
+
+	bool    crashed   = true; // Assume the shot crashed in the ceiling
+	bool    finished  = false;
+	int32_t reached_x = x;
+	int32_t reached_y = y;
+	double  end_xv    = 0.;
+	double  end_yv    = 0.;
+	bool    can_mod_a = true;
+	bool    can_mod_p = true;
+	bool    top_wrap  = false; // Whether the shot wrapped through a wrap ceiling
+	bool    can_dig   =   (weap_idx >= BURROWER)
+	                   && (weap_idx <= PENETRATOR);
+
+	// Cycle until the ceiling isn't hit any more.
+	while ( canWork && !isStopped && crashed
+		&& (can_mod_a || can_mod_p)
+		&& traceShot(curr_angle, finished, top_wrap, reached_x, reached_y,
+	                 end_xv, end_yv)
+		&& finished ) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		crashed = false;
+
+		if ( (reached_y <= BOXED_TOP) // crashed on top
+		    // wrapped to bottom with dirt above is a ceiling crash, too.
+		  || ( top_wrap && !can_dig // (Unless a penetrator trick shot is tried)
+		    && (reached_y > global.surface[reached_x].load()) )
+		    // Steel wall have additional wall crashes
+		  || ( (WALL_STEEL == env.current_wallType)
+		    && ( (reached_x <= 2)
+		      || (reached_x >= (env.screenWidth - 3) ) ) ) ) {
+
+			crashed = true;
+
+			// Either reduce angle (-1), or power (1), or both (0)
+			int32_t mod_mode = RAND_AI_1P ? (rand() % 2 ? -1 : 1) : 0;
+
+			// Apply mods but do not reduce into nothingness
+			if ( (mod_mode < 1) && can_mod_a) {
+				if ( (curr_angle > 90) && (curr_angle < 270) )
+					curr_angle += curr_angle < 180 ? -1 : 1;
+				else
+					can_mod_a = false;
+			}
+			if ( (mod_mode > -1) && can_mod_p) {
+				if (curr_power > MIN_POWER)
+					curr_power -= 5;
+				else
+					can_mod_p = false;
+			}
+
+			DEBUG_LOG_AIM(player->getName(),
+			              "Ceiling crash! Reducing %s [%d°/%d]",
+			              1 == mod_mode ? "power          " :
+			              0 == mod_mode ? "angle and power" :
+			                              "angle          ",
+			              GET_DISP_ANGLE(curr_angle), curr_power)
+
+		}
+	} // End of crashed loop
+
+	// If the last (not crashed) shot is finished but doesn't get to
+	// the target, it is blocked. But this is only considered if
+	// a) This is the last attempt and
+	// b) This map has a wrap or steel ceiling and
+	// c) There was no positive setup score already
+	if ( finished && !crashed
+	  && (best_setup_score <= 0)
+	     // But do not bail out on first try!
+	  && (best_setup_score > NEUTRAL_ROUND_SCORE)
+	  && (weap_idx < WEAPONS)
+	  && (curr_overshoot < 0) // too short
+	  && is_last
+	  && ( (WALL_STEEL == env.current_wallType)
+	    || (WALL_WRAP  == env.current_wallType) )
+	  && (-curr_overshoot > weap_curr->radius) // Can't hit
+	  && (-curr_overshoot > (mem_curr->distance / 3 * 2)) ) {
+		// Note: With big weapons an near opponents, the radius might
+		// be larger than two thirds the distance, hence two checks.
+		bool free_tank = FABSDISTANCE2(x, y, reached_x, reached_y)
+		                 < weapon[RIOT_CHARGE].radius;
+
+		if (useFreeingTool(free_tank, is_last)) {
+			needAim   = false;
+			isBlocked = true;
+			if (free_tank)
+				calcUnbury(is_last);
+			else {
+				// If a riot bomb is chosen, flatten the angle:
+				flattenCurrAng();
+				sanitizeCurr();
+				angle     = curr_angle;
+				power     = curr_power;
+				DEBUG_LOG_AIM(player->getName(),
+							  "Obstacle detected, trying to clear path using %s",
+							  weap_idx < WEAPONS
+							  ? weapon[weap_idx].getName()
+							  : item[weap_idx - WEAPONS].getName())
+			}
+			return true;
+		}
+
+		// This did not work. (useFreeingTool always
+		// succeeds if is_last is true)
+		return false;
+	}
+
+	return (is_last || !crashed);
+}
+
+
+/** @brief calculate a hit score off the dmgDone values in the opponent memory
+  *
+  * This method cycles through the opponents memory, and sums up
+  * the damage done with curr_weap to a total score according
+  * to a) how much damage over the opponents health (aka overkill)
+  * has been done and b) on which team they are compared to us.
+  *
+  * If the primary target was not hit, the score is ensured to be
+  * negative, as collateral damage is discouraged. However, this is
+  * only done if @a is_last is false, as collateral damage with a
+  * total positive score is better than nothing on the very last attempt.
+  *
+  * @param[in] is_last if set to true then any score is accepted.
+  * @return The accumulated score.
+**/
+int32_t AICore::calcHitScore(bool is_last)
+{
+	int32_t    hit_score    = 0;
+	opEntry_t* opp          = mem_head;
+	bool       can_overkill = true;
+	eTeamTypes target_team  = mem_curr
+	                        ? mem_curr->entry->opponent->team
+	                        : TEAM_NEUTRAL;
+	bool       tgt_team_hit = false;
+	weaponType weapType     = static_cast<weaponType>(weap_curr->type);
+
+
+	// Dirt weapons and the reducer can not overkill
+	if ( ( (DIRT_BALL     <= weapType)
+	    && (SUP_DIRT_BALL >= weapType) )
+	  || (  REDUCER       == weapType) )
+		can_overkill = false;
+
+
+	while (opp) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		if (opp->dmgDone > 0) {
+			int32_t overkill  = 0;
+			bool    is_killed = false;
+			bool    shock_hit = (isShocked && (opp->entry == shocker));
+			double  self_mod  = opp->entry->opponent == player
+								? (player->selfPreservation + 1.) * ai_level
+								: 1.;
+			// Note: team_mod is negative if same team, so self_mod must
+			//       be positive, or self hits generate huge scores!
+
+			// 1: Determine whether the opponent was killed.
+			if (can_overkill && (opp->dmgDone >= opp->opLife) ) {
+				overkill     = opp->dmgDone - opp->opLife;
+				opp->dmgDone = opp->opLife;
+				is_killed    = true;
+			}
+
+			DEBUG_LOG_AIM(player->getName(),
+						  "Total damage against %10s: %d (%s, %d overkill)",
+						  opp->entry->opponent->getName(), opp->dmgDone,
+						  is_killed ? "KILLED" : "not killed", overkill)
+
+
+			// 2: Add the simple damage to the score:
+			hit_score += static_cast<double>(opp->dmgDone)
+			           * opp->team_mod * self_mod
+					   * (shock_hit ? ai_over_mod : 1.);
+
+
+			// 3: Raise the score a bit if it is collateral damage on
+			//    non-neutral team members of our target, but not our team.
+			if ( (TEAM_NEUTRAL != target_team)
+			  && (player->team != target_team)
+			  && (opp->entry->opponent->team == target_team) ) {
+				hit_score   *= 1. + ((player->defensive + 2.5) / 10.);
+				tgt_team_hit = true;
+			}
+
+
+			// 4: If the opponent is killed, add a bonus to the score depending
+			//    on team hit, self hit and whether the bot needs money or not.
+			if (is_killed) {
+				double kill_bonus = opp->dmgDone;
+
+				if (needMoney)
+					kill_bonus *= ai_type_mod;
+
+				// Add some more for killing the shocker:
+				if (shock_hit)
+					kill_bonus += kill_bonus / ai_over_mod;
+
+				hit_score += kill_bonus * opp->team_mod * self_mod;
+			}
+
+
+			// 5: Check for overkill and dock points for it.
+			if (overkill > 0) {
+				double over_score = overkill;
+
+				// It is bad if the bot needs money, as it wastes expensive ammo
+				if (needMoney)
+					over_score *= ai_type_mod + .5;
+
+				// On the other hand, if the bot hit the shocker, the overkill
+				// isn't considered that bad, though.
+				if (shock_hit)
+					over_score /= 3. - ai_over_mod;
+
+				// Generate a generally negative score:
+				over_score = std::abs(over_score) * -1.;
+
+				// The more aggressive, the less the reduction will be,
+				// but only if it is neithe rus nor our team that got hit
+				if (!opp->onSameTeam)
+					over_score /= (player->defensive - 2.) * -1.;
+
+				// Add a fraction of the overkill score
+				hit_score += over_score / (10. - ai_level_d);
+			} // End of overkill score
+		} // end of having damage done
+		opp = opp->next;
+	} // End of looping opponents
+
+	// If the primary target was not hit and this is not the last
+	// attempt, make hit_score to be negative, unless the enemy
+	// team is decimated. In the latter case the score is simply
+	// reduced according to whether the bot needs money or not.
+	if (!curr_prime_hit && !is_last && (hit_score > 0.)) {
+		if (tgt_team_hit)
+			hit_score /= (ai_type_mod + ai_level_d)
+			             / (player->defensive + (needMoney ? 2.5 : 4.0));
+		else
+			hit_score = -1 * std::abs(hit_score);
+	}
+
+	return hit_score;
+}
+
+
+/** @brief calculate a score according to where the shot hit and
+  * collateral damage done to friend and foe.
+  *
+  * Damage is recorded in the opponent memory dmgDone value.
+  *
+  * @param[in] hit_x x coordinate where the current selection hit.
+  * @param[in] hit_y y coordinate where the current selection hit.
+  * @param[in] weap_rad Calculated radius of the weapon.
+  * @param[in] dmg Calculated damage of the weapon.
+  * @param[in] weapType Type of the weapon.
+  * @return The resulting score
+**/
+void AICore::calcHitDamage(int32_t hit_x, int32_t hit_y, double weap_rad,
+                           double dmg, weaponType weapType)
+{
+	if ( (nullptr == weap_curr) // no weapon no score
+	  || (0 == weap_rad) )      // no radius, no hit
+		return;
+
+	// If this has no damage it is either a dirt ball, a reducer
+	// or a riot weapon.
+	// As dirt balls and reducers must be evaluated, they get a fake
+	// damage of their radius so a score can be generated.
+	if (0 == dmg) {
+		if ( ( (DIRT_BALL     <= weapType)
+		    && (SUP_DIRT_BALL >= weapType) )
+		  || (  REDUCER       == weapType) )
+			dmg = weap_rad;
+		else
+			return;
+	}
+
+	// Napalm blobs have a much higher full damage output
+	// than listed, as they do damage over time:
+	if (NAPALM_JELLY == weapType)
+		dmg *= static_cast<double>(EXPLOSIONFRAMES * weapon[NAPALM_JELLY].etime)
+		       / ai_over_mod;
+
+	// Now the score can be calculated
+	opEntry_t* opp = mem_head;
+	DEBUG_LOG_AIM(player->getName(), "Checking impact at %d x %d", hit_x, hit_y)
+
+	while (opp) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		TANK* oppTank = opp->entry->opponent->tank;
+
+		if (oppTank) {
+			// Now calculate the score if in range
+			double part_dmg = 0.;
+
+			// For dirt balls, only whether the tank is in x-range counts
+			// as dirt falls down
+			if ( (DIRT_BALL     <= weapType)
+			  && (SUP_DIRT_BALL >= weapType)
+			  && (oppTank->x > (hit_x - weap_rad))
+			  && (oppTank->x < (hit_x + weap_rad)) )
+				part_dmg = dmg;
+			else
+				// All others need a normal check
+				part_dmg = get_hit_damage(oppTank, weapType, hit_x, hit_y);
+
+			if (part_dmg > 0.) {
+
+				// REDUCER must be set, it is only checked as valid, yet:
+				if (REDUCER == weapType)
+					part_dmg = opp->entry->opponent->damageMultiplier * 25.;
+
+				// Set hit damage according to primary or collateral damage
+				if (opp == mem_curr) {
+					curr_prime_hit = true;
+					opp->dmgDone += ROUND(part_dmg);
+					DEBUG_LOG_AIM(player->getName(),
+								  "%10s in blast range : primary damage   : %4d, total %d",
+								  opp->entry->opponent->getName(),
+								  ROUND(part_dmg), opp->dmgDone)
+				} else {
+					opp->dmgDone += ROUND(part_dmg);
+					DEBUG_LOG_AIM(player->getName(),
+								  "%10s in blast range : collateral damage: %4d, total %d",
+								  opp->entry->opponent->getName(),
+								  ROUND(part_dmg), opp->dmgDone)
+				}
+
+			} // end of having damage delivered
+		} // end of having a tank to consider
+
+		opp = opp->next;
+	}
+}
+
+
+/** @brief Case c) Kamikaze
+  *
+  * Note: calcAttack() has to make sure this method is only called if it is
+  * appropriate. No further checks are made within this method.
+  *
+  * @param[in] is_last If this is set to true, the method is forced to succeed.
+  * @return true if sane values could be found.
+**/
+bool AICore::calcKamikaze(bool is_last)
+{
+	DEBUG_LOG_AIM(player->getName(), "I have decided to go bye bye!", 0)
+
+	// Is the selection sane?
+	if (weap_curr->kamikaze) {
+
+		bool is_good = true;
+
+		// Only weapons need any angle/power adaptation
+		if (weap_curr) {
+			if ( (SHAPED_CHARGE <= weap_idx)
+			  && (CUTTER        >= weap_idx) ) {
+
+				// For this it is necessary to look at the terrain.
+				// This does not make sense if there isn't a flat area
+				// at either side of the tank with an even height.
+				int32_t bottom = tank->getBottom();
+				int32_t max_rad = weapon[weap_idx].radius / 20;
+
+				// With a power of 150, the weapon can be hurled ~45 pixels.
+				// So check that place plus some pixels around according
+				// to ai_level.
+				int32_t to_go  = 1 + ai_level + (RAND_AI_1P);
+				int32_t dist   = 45 - (to_go / 2);
+				int32_t diff_l = 0;
+				int32_t diff_r = 0;
+				int32_t xl     = x - dist;
+				int32_t xr     = x + dist;
+
+				for (int32_t i = 0; i < to_go; ++i) {
+					--xl;
+					++xr;
+
+					// If any x is in/ beyond a non-wrap wall, screw it:
+					// a - check left side
+					if ( xl < 1 ) {
+						if (WALL_WRAP == env.current_wallType)
+							xl = env.screenWidth - 1 - (1 - std::abs(xl));
+						else
+							diff_l += env.screenHeight;
+					} else
+						diff_l += std::abs(global.surface[xl].load() - bottom);
+
+					// b - check right side
+					if ( xr > (env.screenWidth - 2) ) {
+						if (WALL_WRAP == env.current_wallType)
+							xr = 1 + (env.screenWidth - 1 - xr);
+						else
+							diff_r += env.screenHeight;
+					} else
+						diff_r += std::abs(global.surface[xr].load() - bottom);
+				} // End of looping distance to_go
+
+				// The average distance is taken. This ignores sudden peaks
+				// and gaps that might stop/swallow the projectile, but
+				// that should be okay.
+				diff_l /= to_go;
+				diff_r /= to_go;
+
+				int32_t a_mod = rand() % (10 - ai_level)
+							  * (rand() % 2 ? -1 : 1);
+				int32_t p_mod = rand() % (20 - (2 * ai_level))
+							  * (rand() % 2 ? -1 : 1);
+				if ( (diff_l < diff_r) && (diff_l < max_rad) )
+					curr_angle = 225 + a_mod;
+				else if (diff_r < max_rad)
+					curr_angle = 135 + a_mod;
+				else
+					is_good = false; // May need emergency plan below
+
+				// Set power and write back values
+				if (is_good)
+					curr_power = 150 + p_mod;
+
+				DEBUG_LOG_AIM(player->getName(),
+				              "Firing %s at %d° with power %d%s",
+				              weapon[weap_idx].getName(),
+				              GET_DISP_ANGLE(curr_angle), curr_power,
+				              is_good ? "!" : " will not work! Need a plan!")
+
+			} else if ( (SML_NAPALM <= weap_idx)
+					 && (LRG_NAPALM >= weap_idx) ) {
+
+				// Adapt according to the wind:
+				int32_t wind     = ROUND(global.wind);
+				int32_t wind_mod = 10 + (std::abs(wind) * (1 + RAND_AI_0P));
+
+				if (wind > 0)
+					curr_angle = 225;
+				else if (wind < 0)
+					curr_angle = 135;
+				else
+					curr_angle = 180;
+
+				curr_power = 100 + wind_mod;
+
+				DEBUG_LOG_AIM(player->getName(),
+				              "Firing %s at %d° with power %d (wind %d, wind_mod %d)",
+				              weapon[weap_idx].getName(),
+				              GET_DISP_ANGLE(curr_angle), curr_power,
+				              wind, wind_mod)
+
+			} else {
+				int32_t spread_mod = 50 + (10 * (1 + RAND_AI_0P));
+				int32_t spread     = weapon[weap_idx].spread; // shortcut
+				curr_angle = 180;
+				curr_power = 200 + ( (spread * spread_mod) / (2 - (spread % 2)));
+
+				DEBUG_LOG_AIM(player->getName(),
+				              "Firing %s at %d° with power %d",
+				              weapon[weap_idx].getName(),
+				              GET_DISP_ANGLE(curr_angle), curr_power)
+			}
+		}
+
+		if (is_good) {
+			sanitizeCurr();
+			angle = curr_angle;
+			power = curr_power;
+			return true;
+		}
+	} // end of sane item/weapon selection
+
+	// No, this selection does not make sense.
+	// However, if this is a last try, it must work somehow.
+	if ( is_last
+	  && ( useItem(ITEM_FATAL_FURY)
+	    || useItem(ITEM_DYING_WRATH)
+	    || useItem(ITEM_VENGEANCE)
+	    || useWeapon(DTH_HEAD)
+	    || useWeapon(NUKE)
+	    || useWeapon(SML_NUKE)
+	    || useWeapon(LRG_MIS)
+	    || useWeapon(MED_MIS)
+	    || useWeapon(SML_MIS) ) ) {
+		// Note: The trick is, that the first working selection
+		// results in the if-statement to end, and SML_MIS always
+		// works.
+		curr_angle = 180;
+		curr_power = 250;
+		angle      = curr_angle;
+		power      = curr_power;
+
+		DEBUG_LOG_AIM(player->getName(),
+					  "Emergency plan: Firing %s at %d° with power %d",
+					  weapon[weap_idx].getName(),
+					  GET_DISP_ANGLE(curr_angle), curr_power)
+
+		return true;
+	}
+
+	// This can not work, and does not need to be forced.
+	return false;
+}
+
+
+/** @brief Case a) The tank is not buried (enough) and a laser is chosen.
+  *
+  * Note: calcAttack() has to make sure this method is only called if it is
+  * appropriate. No further checks are made within this method.
+  *
+  * @return True if the opponent can be hit, false if the shot is blocked or
+  *         pumped into a wall or the ceiling.
+**/
+bool AICore::calcLaser(bool is_last)
+{
+	int32_t old_angle = curr_angle;
+	int32_t old_power = curr_power;
+	double  drift     = static_cast<double>(maxAiLevel + 2 - RAND_AI_0P)
+	                  * errorMultiplier * (rand() % 2 ? -1. : 1.);
+
+	curr_power = tank->p;
+	curr_angle = GET_SAFE_ANGLE(mem_curr->opX - x, mem_curr->opY - y, drift);
+
+	// Power doesn't matter, but the values must be sane nonetheless:
+	sanitizeCurr();
+
+	// Lets see where the laser ends:
+	double start_x = 0;
+	double start_y = 0;
+	player->tank->getGuntop(curr_angle, start_x, start_y);
+
+	BEAM mind_beam(player, start_x, start_y, curr_angle, weap_curr->type,
+	               BT_MIND_SHOT);
+
+	int32_t end_x = 0;
+	int32_t end_y = 0;
+	mind_beam.getEndPoint(end_x, end_y);
+
+	// Generate a score for this
+	curr_prime_hit = false;
+	// Note: calcHitDamage() sets curr_prime_hit to true if we hit our target.
+
+	calcHitDamage(end_x, end_y, weapon[weap_curr->type].radius,
+	              weap_curr->dmgSingle, static_cast<weaponType>(weap_curr->type));
+	int32_t hit_score = calcHitScore(is_last && needSuccess);
+
+	// If the target is behind a dirt wall, break up this attempt
+	bool crashed = false;
+	if ( !tank->shootClearance(curr_angle, mem_curr->distance, crashed)
+	  || crashed) {
+
+		// ...unless this is a forced success ...
+		if (is_last)
+			// at least reduce the score.
+			hit_score = hit_score;
+		else {
+			curr_angle       = old_angle;
+			curr_power       = old_power;
+			needAim          = true; // for the next try or the closure
+			DEBUG_LOG_AI(player->getName(), "Cancelling laser shot: %s",
+			             crashed ? "Wrong angle" : "Not enough clearance")
+			return false;
+		}
+	}
+
+	// If a positive score was achieved, we are set
+	if ( (hit_score > 0) || is_last) {
+		// Write back values
+		sanitizeCurr();
+		angle            = curr_angle;
+		power            = curr_power;
+		if ( (hit_score > best_round_score) || is_last)
+			best_round_score = std::abs(hit_score);
+
+		DEBUG_LOG_AIM(player->getName(),
+						"Firing %s at %d° with power %d",
+						weapon[weap_curr->type].getName(),
+						GET_DISP_ANGLE(curr_angle), curr_power)
+
+		return true;
+	}
+
+	curr_angle       = old_angle;
+	curr_power       = old_power;
+	needAim          = true; // for the next try or the closure
+	DEBUG_LOG_AI(player->getName(), "Cancelling laser shot: %d score too low",
+	             hit_score)
+
+	return false;
+}
+
+
+/** @brief calculate x and y offsets for weapons that need it.
+  *
+  * These offsets are stored in offset_x and offset_y, as they are needed
+  * in multiple places.
+  *
+  * If the needed offset is off the screen, or makes no sense, the method
+  * returns false. But if @a is_last is set to true, insane offsets are tried
+  * to be fixed. The idea is, that the bot tries nevertheless out of pure
+  * desperation.
+  *
+  * @param[in] is_last If set to true, the method never fails
+  * @return true if sane offsets were found.
+**/
+bool AICore::calcOffset(bool is_last)
+{
+	bool result = true;
+
+	offset_x = 0;
+	offset_y = 0;
+
+	/* Weapon type 1: Napalm bombs
+	 * There are two situations to consider, the normal shot using wind
+	 * and the under-run trick. The latter is evil and will almost always
+	 * destroy any tank even with a small napalm bomb. Just place the
+	 * bomb with tail wind under a tank. No shield can protect the tank
+	 * and most jellies will burn into the hull. So this option must be
+	 * limited.
+	 * Otherwise the wind is what has to be taken into account plus the
+	 * surroundings. If the wind side is above the opponent, more distance
+	 * is needed than when the area is below the target tank.
+	 */
+	if ( (weap_idx >= SML_NAPALM) && (weap_idx <= LRG_NAPALM) ) {
+		offset_x = global.wind * (ai_level + RAND_AI_1P) * (-1. - focusRate);
+
+		// The farther away the opponent is, the more power is needed to
+		// bring the package to the target. More impact power means a higher
+		// initial velocity of the blobs, so the offset must be tweaked a bit.
+		offset_x *= 1.
+		          + ( mem_curr->distance
+		            / static_cast<double>(env.screenWidth)
+		            * focusRate);
+
+
+		int32_t pos_x = ROUND(mem_curr->opX) + offset_x;
+
+
+		// If the resulting x position is not on the screen,
+		// the calculation already failed.
+		if (pos_x < 2) {
+			if (is_last) {
+				// But this must be taken...
+				pos_x    = 2;
+				offset_x = pos_x - ROUND(mem_curr->opX);
+			} else
+				result = false;
+		} else if (pos_x > (env.screenWidth - 2)) {
+			if (is_last) {
+				// The same...
+				pos_x    = env.screenWidth - 2;
+				offset_x = ROUND(mem_curr->opX) - pos_x;
+			} else
+				result = false;
+		}
+
+		// If the result is true, the area around pos_x must be checked
+		// to determine the y offset and whether to adapt offset_x
+		// any further or not.
+		if (result) {
+			bool    found = false;
+			int32_t pos_y = global.surface[pos_x].load();
+
+			if (pos_y < (mem_curr->opY - std::abs(offset_x) + ai_level)) {
+				// This means more distance is needed. So we search for
+				// the next valid x position that goes down again or that
+				// doubles the x distance, whatever comes earlier.
+				int32_t mov_x = SIGN(global.wind) * -1;
+				int32_t max_x = pos_x + offset_x;
+
+				if (max_x < 2)
+					max_x = 2;
+				if (max_x > (env.screenWidth - 2))
+					max_x = (env.screenWidth - 2);
+
+				for ( ; !found && (pos_x != max_x); pos_x += mov_x) {
+					if (global.surface[pos_x].load() > pos_y) {
+						pos_x -= mov_x; // One step back
+						found  = true;
+					}
+				}
+			} else if (pos_y > (mem_curr->opY + std::abs(offset_x) - ai_level)) {
+				// Here check the area towards the enemy. But going nearer
+				// than half the distance means an under-run, which is only
+				// allowed after an additional check.
+				int32_t mov_x = SIGN(global.wind);
+				int32_t max_x = pos_x - (offset_x / (RAND_AI_0P ? 1 : 2));
+				// Note: Yes, the RAND_AI_0P is the mentioned additional check. ;)
+				int32_t max_y = mem_curr->entry->opponent->tank->getBottom();
+
+				for ( ; !found && (pos_x != max_x); pos_x += mov_x) {
+					if (global.surface[pos_x].load() <= (max_y - ai_level))
+						found = true;
+				}
+			}
+
+			// adapt offset_x and offset_y now:
+			offset_x = pos_x - mem_curr->opX;
+			offset_y = global.surface[pos_x].load() - mem_curr->opY;
+		}
+	} // End of handling napalm
+
+	/* Weapon type 2: Shaped charges.
+	 * These are easy. The shaped charge have an y radius of 1/20 of the
+	 * x radius. Within this y radius of the centre no damage is done, so
+	 * the area from this radius + 1 to + (ai_level * 2) is checked on
+	 * either sides whether there is an y position that allows to actually
+	 * catch the enemy in the blast. If not, this try is a failure.
+	 * However, there is another trick shot here: Place a big shaped charge
+	 * like the cutter at the right height behind a hill and blast the
+	 * opponents tank through it.
+	 */
+	else if ( (weap_idx >= SHAPED_CHARGE) && (weap_idx <= CUTTER) ) {
+		int32_t rad_y    = weapon[weap_idx].radius / 20;
+		int32_t dist_x   = rad_y + RAND_AI_1P;
+		int32_t max_dist = RAND_AI_0P
+		                 ? (dist_x * 2) + RAND_AI_1P        // normal shot
+		                 : weapon[weap_idx].radius * 2 / 3; // trick shot
+		int32_t seek_y   = (mem_curr->opY
+		                   + mem_curr->entry->opponent->tank->getBottom()) / 2;
+		int32_t left_x  = mem_curr->opX - dist_x;
+		int32_t right_x = mem_curr->opX + dist_x;
+		int32_t left_y  = left_x > 2
+						? std::abs(global.surface[left_x].load()) : 0;
+		int32_t right_y = right_x < (env.screenWidth - 2)
+						? std::abs(global.surface[right_x].load()) : 0;
+		bool    go_left  = (mem_curr->opX > x); // Which side to prefer
+		bool    found_l  = false;
+		bool    found_r  = false;
+
+		for ( ; !found_l && !found_r && (dist_x < max_dist); ++dist_x) {
+			left_x  = mem_curr->opX - dist_x;
+			right_x = mem_curr->opX + dist_x;
+			left_y  = left_x > 2
+			        ? std::abs(global.surface[left_x].load()) : 0;
+			right_y = right_x < (env.screenWidth - 2)
+			        ? std::abs(global.surface[right_x].load()) : 0;
+
+			if (std::abs(left_y - seek_y) <= rad_y)
+				found_l = true;
+			if (std::abs(right_y - seek_y) <= rad_y)
+				found_r = true;
+		}
+
+		// If both are valid, use what is preferred
+		if (found_l && found_r) {
+			if (go_left)
+				found_r = false;
+			else
+				found_l = false;
+		}
+
+		// if none is found but this is the last_try, take the simple
+		// distance to the preferred side
+		if (!found_l && !found_r) {
+			if (is_last) {
+				if ( (go_left && ((mem_curr->opX - rad_y - 1) > 1))
+				  || ((mem_curr->opX + rad_y + 1) > (env.screenWidth - 2)) ) {
+					found_l = true;
+					left_x  = mem_curr->opX - rad_y - 1;
+					left_y  = global.surface[left_x].load();
+				} else {
+					found_r = true;
+					right_x = mem_curr->opX + rad_y + 1;
+					right_y = global.surface[right_x].load();
+				}
+			} else
+				result = false;
+		}
+
+		// If something is found, set the real offsets
+		if (found_l) {
+			offset_x = left_x - mem_curr->opX;
+			offset_y = left_y - mem_curr->opY;
+		} else if (found_r) {
+			offset_x = right_x - mem_curr->opX;
+			offset_y = right_y - mem_curr->opY;
+		}
+	} // End of handling shaped charges
+
+	/* Weapon type 3: Driller
+	 * The driller must be placed above an enemy tank. This means it is
+	 * only useful if the tank is buried, or the tank shall be sunk into
+	 * the surface. However, there might be the possibility of an under
+	 * shot if the tank is placed on a mountain side.
+	 */
+	else if (DRILLER == weap_idx) {
+		int32_t rad_x    = weapon[weap_idx].radius / 20;
+		int32_t pos_x    = ROUND(mem_curr->opX);
+		int32_t pos_y    = global.surface[pos_x].load();
+		int32_t max_dist = rad_x * 2 / 3;
+		int32_t min_y    = mem_curr->opY - rad_x;
+		int32_t max_y    = mem_curr->entry->opponent->tank->getBottom() + rad_x;
+		bool    found_l  = false;
+		bool    found_r  = false;
+
+		// If the direct coordinates are already in order, do not search
+		// further. Otherwise try to shift left and right.
+		if ( (pos_y > min_y) && (pos_y < max_y) ) {
+			for (int32_t off_x = 1
+			    ; !found_l && !found_r && (off_x < max_dist)
+			    ; ++off_x) {
+
+				int32_t left_x  = pos_x - off_x;
+				int32_t right_x = pos_x + off_x;
+				int32_t left_y  = left_x > 1
+				                ? global.surface[left_x].load() : mem_curr->opY;
+				int32_t right_y = right_x < (env.screenWidth - 1)
+				                ? global.surface[right_x].load() : mem_curr->opY;
+				if ( (left_y < min_y) || (left_y > max_y) ) {
+					found_l = true;
+					pos_x   = left_x;
+					pos_y   = left_y;
+				} else if ( (right_y < min_y) || (right_y > max_y) ) {
+					found_r = true;
+					pos_x   = right_x;
+					pos_y   = right_y;
+				}
+			}
+
+			// If this did not succeed, but it is the last_shot or
+			// the AI chooses to bury its opponent, use the opponents
+			// coordinates.
+			if (!found_l && !found_r) {
+				if (is_last || (RAND_AI_0N)) {
+					pos_x = mem_curr->opX;
+					pos_y = mem_curr->opY;
+				} else
+					result = false;
+			}
+		} // end of searching a position to use
+
+		// Set offsets if all is well
+		if (result) {
+			offset_x = pos_x - mem_curr->opX;
+			offset_y = pos_y - mem_curr->opY;
+		}
+	} // End of handling drillers
+
+	return result;
+}
+
+
+/** @brief Case d) Fire in non-boxed mode
+  *
+  * Note: calcAttack() has to make sure this method is only called if it is
+  * appropriate. No further checks are made within this method.
+  *
+  * @param[in] is_last If this is set to true, the method is forced to succeed.
+  * @param[in] allow_flip_shot If set to true, the bot is allowed to shoot in
+  * the opposite direction. On steel walls, this parameter is ignored.
+  * @return true if sane values could be found.
+**/
+bool AICore::calcStandard(bool is_last, bool allow_flip_shot)
+{
+	bool result = calcOffset(is_last);
+	needAim = true;
+
+
+	// --- 1) Get a basic raw angle firing directly ---
+	// ------------------------------------------------
+	bool    wrapped  = false;
+	double  opX      = mem_curr->opX + offset_x;
+	double  opY      = mem_curr->opY + offset_y;
+	// just some shortcuts
+	double  dist_x   = opX - x;
+	double  dist_y   = opY - y;
+	int32_t scrWidth = env.screenWidth;
+
+	// Do not start horizontally, this might happen quite often.
+	// If the opponent is above, limit the angle to somewhere between
+	// 60° and 75°. If it is below, limit angle between 20° and 35° and
+	// limit the angle between 40° and 55° if ~equal.
+	int32_t new_angle = GET_SAFE_ANGLE(dist_x, dist_y, 0);
+	int32_t ang_limit = (focusRate * static_cast<double>(rand() % 16));
+
+	if      (dist_y < -100) /* above */ ang_limit += 60;
+	else if (dist_y >  100) /* below */ ang_limit += 20;
+	else                    /* equal */ ang_limit += 40;
+
+	// Apply limit:
+	if (new_angle < ( 90 + ang_limit)) new_angle =  90 + ang_limit;
+	if (new_angle > (270 - ang_limit)) new_angle = 270 - ang_limit;
+
+
+	// --- 2) Modify the beginning angle according to focusRate ---
+	// --- Keeping this more variable gives a larger range of   ---
+	// --- starting points to go forth from.                    ---
+	// ------------------------------------------------------------
+	double angle_mod = (rand() % 13) * focusRate // useless: 0-2, deadly+1: 0-12
+	                 * ((rand() % 2) ? -1. : 1.);
+	while ( (std::abs(angle_mod > 0.))
+	     && ( ( (new_angle > 180)
+	         && ( ( (new_angle + angle_mod) < 190)
+	           || ( (new_angle + angle_mod) > 260) ) )
+	       || ( (new_angle < 180)
+	         && ( ( (new_angle + angle_mod) > 170)
+	           || ( (new_angle + angle_mod) < 100) ) ) ) ) {
+		angle_mod /= 2.;
+	}
+	new_angle += angle_mod;
+
+
+	// --- 3) If this is a wrap wall, check whether shooting ---
+	// ---    through the wall is actually shorter.          ---
+	// --- A note on allow_flip_shot: If shooting wrapped is ---
+	// --- shorter, the AI will chose it more often the      ---
+	// --- higher the AI level. (80% for a deadly bot)       ---
+	// --- The flipping is then a possibility to shoot non-  ---
+	// --- wrapped again.                                    ---
+	// ---------------------------------------------------------
+	if ( (WALL_WRAP == env.current_wallType) && RAND_AI_0P ) {
+		int32_t wrapDist = opX > x
+		                 ?  x + scrWidth - 3 - opX
+		                 : (scrWidth - x - 3 + opX) * -1;
+
+		if (std::abs(wrapDist) < std::abs(dist_x)) {
+			wrapped    = true;
+			dist_x     = wrapDist;
+			new_angle = FLIP_ANGLE(new_angle);
+
+			DEBUG_LOG_AIM(player->getName(),
+			              "Flipping through wrap wall at %d°",
+			              GET_DISP_ANGLE(new_angle))
+		}
+	}
+
+
+	// --- 4) Switch sides if possible and allowed ---
+	// -----------------------------------------------
+	if ( (WALL_STEEL != env.current_wallType)
+	  && allow_flip_shot
+	  && (rand() % ( (ai_level + 3) / 2)) ) {
+		new_angle = FLIP_ANGLE(new_angle);
+
+		// The result of this flip is different for each wall type
+		if (WALL_RUBBER == env.current_wallType) {
+			dist_x += opX > x
+			        ? (x - 1.) + ( (x - 1.) / BOUNCE_CHANGE)
+			        :    (scrWidth - opX - 2.)
+			        + ((scrWidth - opX - 2.) / BOUNCE_CHANGE);
+		} else if (WALL_SPRING == env.current_wallType) {
+			dist_x += opX > x
+			        ? (x - 1.) + ( (x - 1.) / SPRING_CHANGE)
+			        :    (scrWidth - opX - 2.)
+			          + ((scrWidth - opX - 2.) / SPRING_CHANGE);
+		} else if (WALL_WRAP == env.current_wallType) {
+			if (wrapped)
+				// Shoot directly again
+				dist_x = opX - x;
+			else
+				dist_x = opX > x
+					   ?  x + scrWidth - 3 - opX
+					   : (scrWidth - x - 3 + opX) * -1;
+		}
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "Flipping %s wall at %d°",
+		              wrapped ? "back from" : "towards",
+		              GET_DISP_ANGLE(new_angle))
+
+		// wrap / unwrap
+		wrapped = !wrapped;
+	}
+
+
+	// --- 5) Adjust angle giving shooting clearance    ---
+	// --- Here the clearance is either needed to reach ---
+	// --- the next wall or half the distance to the    ---
+	// --- selected opponent.                           ---
+	// ----------------------------------------------------
+	int32_t clearance = std::abs(dist_x / 2);
+	int32_t old_angle = new_angle;
+	int32_t max_drift = (ai_level + 1) / 2; // [1;3]
+	bool    crashed   = false;
+
+	if (wrapped)
+		// wrapped shots need clearance to the wall away from the opponent:
+		clearance = opX > x ? x - 2 : env.screenWidth - x - 2;
+
+	while ( (new_angle < (180 - max_drift))
+	     && !tank->shootClearance(new_angle, clearance, crashed)
+	     && !crashed )
+		++new_angle;
+	while ( (new_angle > (180 + max_drift))
+	     && !tank->shootClearance(new_angle, clearance, crashed)
+	     && !crashed)
+		--new_angle;
+
+
+	// --- 6) Revert to half the distance between both angles ---
+	// ---    if no full clearance is possible.               ---
+	// --- An attempt to remove possible obstacles might be   ---
+	// --- triggered here.                                    ---
+	// ----------------------------------------------------------
+	if ( ( new_angle >= (180 - max_drift) )
+	  && ( new_angle <= (180 + max_drift) ) ) {
+		new_angle = (new_angle + old_angle) / 2;
+
+		// If this is the last chance, try to clear the obstacle.
+		// Alternatively a bot with high pain sensitivity might chose
+		// to remove the obstacle earlier. The idea here is, that the
+		// bot does not want to "piss off" its opponent before the
+		// obstacle is removed.
+		// However, if there is already a setup with a positive
+		// score, revert to that.
+		if ( (best_setup_score <= 0)
+		  && (is_last
+		    || (  (rand() % (ai_level * 20))
+		        < (player->painSensitivity * ai_level * 5) ) ) ) {
+			/* Range is from Useless and pain resistant (0.1) to
+			 * (Deadly + 1) and very pain sensitive: [max rand value]
+			 * Useless    : 0.1 * 1 * 5 =  0.5 [ 20]
+			 * Deadly + 1 : 3.0 * 6 * 5 = 90.0 [120]
+			 */
+			isBlocked  = true;
+			result     = useFreeingTool(false, is_last);
+			curr_angle = new_angle;
+
+			// Try not to bomb the ceiling:
+			if ( (curr_angle >= 150) && (curr_angle <= 210)) {
+				flattenCurrAng();
+
+				// Write back curr_ang, or it gets overwritten below.
+				new_angle = curr_angle;
+			}
+
+			DEBUG_LOG_AIM(player->getName(),
+						  "Obstacle detected, trying to clear path using %s",
+						  weap_idx < WEAPONS
+						  ? weapon[weap_idx].getName()
+						  : item[weap_idx - WEAPONS].getName())
+		} else
+			// This did not work out
+			result = false;
+	} // End of being blocked
+
+
+	// --- 7) Find necessary power                ---
+	// --- This is just an estimation on possible ---
+	// ---  "air time" of the projectile          ---
+	// ----------------------------------------------
+	if (result) {
+		// As there is nothing that can fail now, the new
+		// angle is the one to go with:
+		curr_angle = new_angle;
+
+		double slope_x = env.slope[curr_angle][0];
+		double slope_y = env.slope[curr_angle][1];
+		double rawTime = slope_x != 0.
+		               ? dist_x / slope_x
+		               : dist_x / 0.00000001; // 180° should be impossible though.
+
+		// lower target, less power.
+		// If the target is above, the projectile hits earlier than
+		// on lower targets, where the projectile has to fall down there.
+		double airTime = std::abs(rawTime) + (
+		                 dist_y * slope_y * env.gravity
+		                 * env.FPS_mod * 2.0);
+
+		// Less airTime doesn't necessarily mean less power
+		// Horizontal firing means more power needed even though
+		// air time is minimised.
+		curr_power = ROUNDu(std::sqrt(
+						airTime * env.gravity * env.FPS_mod)
+						* static_cast<double>(env.frames_per_second));
+
+		// Power modification according to the bots focus rate
+		// This helps having slightly different starting powers to
+		// begin aiming with.
+		curr_power += focusRate * static_cast<double>(rand() % 51)
+		            * (rand() % 2 ? -1. : 1.);
+		// With a focus rate of [0.166;1] this results in a modification
+		// between [-8.3;8.3] and [-50;50].
+
+		// Be sure power is in the valid range:
+		if (curr_power > MAX_POWER) curr_power = MAX_POWER;
+		if (curr_power < MIN_POWER) curr_power = MIN_POWER;
+
+		// Power is only available in a stepping of five
+		curr_power -= curr_power % 5;
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "Firing %s at %d° with power %d",
+		              weapon[weap_idx].getName(),
+		              GET_DISP_ANGLE(curr_angle), curr_power)
+	} // End of having a result
+
+	// Write back values and stop aiming if this is a blocked path freeing attempt
+	if (result && isBlocked) {
+		sanitizeCurr();
+		angle   = curr_angle;
+		power   = curr_power;
+		needAim = false;
+	}
+
+
+	return result;
+}
+
+
+/** @brief Case b) The tank is buried and an appropriate tool is chosen
+  *
+  * Note: This means that it must be checked whether this is an appropriate
+  * tool or not. Here the method might fail if it isn't suitable.
+  *
+  * @param[in] is_last If this is set to true, the method is forced to succeed.
+  * @return true if sane values could be found.
+**/
+bool AICore::calcUnbury(bool is_last)
+{
+	DEBUG_LOG_AIM(player->getName(), "I am buried! (%d >= %d)",
+	              buried, BURIED_LEVEL)
+
+	// Suitable tool?
+	if ( ( (RIOT_BOMB  <= weap_curr->type)       // Clear freeing tool
+	    && (RIOT_BLAST >= weap_curr->type) )     // that clears dirt
+	  || ( ( (SHAPED_CHARGE > weap_curr->type)   // shaped charges can not
+	      || (CUTTER        < weap_curr->type) ) // be used, and neither can
+	    && (DRILLER != weap_curr->type)          // the driller, to self
+		&& weap_curr->kamikaze) ) {              // destruct while buried
+
+		// To not blast away an obstacle towards a wall with no
+		// enemies behind it, count how many enemies are on each
+		// side, first:
+		opEntry_t* op       = mem_head;
+		int32_t    op_left  = 0;
+		int32_t    op_right = 0;
+		while (op) {
+			if ( op->alive && !op->onSameTeam ) {
+				if (op->opX < tank->x)
+					op_left++;
+				else
+					op_right++;
+			}
+			op = op->next;
+		}
+
+		// Determine starting values according to which side
+		// is buried stronger, and where the opponents are:
+		bool go_left = true;
+
+		// It is better to go right instead of left if:
+		// a) more enemies are on the right
+		// b) the count is equal but the right side is more buried or
+		// c) the current favourite target is on the right and the left
+		//    is not that much more buried. (depends on AI level)
+		if ( (op_right > op_left)                               // a)
+		  || ( (op_right == op_left) && (buried_r > buried_l) ) // b)
+		  || ( (mem_curr->opX > x)
+		    && (std::abs(buried_r - buried_l) < ai_level) ) ) { // c)
+
+			go_left = false;
+		}
+
+		// find a good starting angle where the obstacle begins:
+		int32_t dist    = ai_level * (player->defensive + 3.) * 2;
+		bool    crashed = false;
+		curr_angle = 180;
+
+		if (go_left) {
+			while ( (curr_angle < 250)
+			     && ( tank->shootClearance(curr_angle, dist, crashed)
+			       || !crashed) )
+				++curr_angle;
+		} else {
+			while ( (curr_angle > 110)
+			     && ( tank->shootClearance(curr_angle, dist, crashed)
+			       || !crashed) )
+				--curr_angle;
+		}
+
+		// Add a variant to the angle:
+		curr_angle += ( (rand() % 21) - 10) / ai_level;
+
+		// If riot charges are used, 45° is the lower border.
+		if ( (RIOT_BOMB  <= weap_curr->type)
+		  && (RIOT_BLAST >= weap_curr->type) ) {
+			if (curr_angle < 135) curr_angle = 135;
+			if (curr_angle > 225) curr_angle = 225;
+		}
+
+		// Be sure current values are sane:
+		sanitizeCurr();
+
+		angle     = curr_angle;
+		power     = curr_power;
+		needAim   = false; // Already done here!
+		isBlocked = true;
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "Freeing myself using %s at %d° with power %d",
+		              weapon[weap_idx].getName(),
+		              GET_DISP_ANGLE(angle), power)
+
+		return true;
+	} // End of having selected an appropriate tool.
+
+	// The only non-self-destruct way to use a weapon for freeing
+	// one self is the shaped charge:
+	if ( ( (SHAPED_CHARGE <= weap_curr->type)
+	    && (CUTTER        >= weap_curr->type) )
+	  || ( DRILLER == weap_curr->type ) ) {
+		curr_angle = 180;
+		curr_power = 10 + RAND_AI_0P;
+
+		sanitizeCurr();
+
+		angle     = curr_angle;
+		power     = curr_power;
+		needAim   = false; // Already done here!
+		isBlocked = true;
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "Freeing myself using %s at %d° with power %d",
+		              weapon[weap_idx].getName(),
+		              GET_DISP_ANGLE(angle), power)
+
+		return true;
+	}
+
+	// emergency values if this is our last try:
+	if (is_last) {
+		if (!useFreeingTool(true, true))
+			useWeapon(SML_MIS);
+		curr_angle = buried_l > buried_r ? 200 : 100
+		           + (rand() % 61);
+		curr_power = 500 + (rand() % 501);
+
+		sanitizeCurr();
+
+		angle     = curr_angle;
+		power     = curr_power;
+		needAim   = false; // Already done here!
+		isBlocked = true;
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "(last!) Freeing myself using %s at %d° with power %d",
+		              weapon[weap_idx].getName(),
+		              GET_DISP_ANGLE(angle), power)
+
+		return true;
+	}
+
+	// Otherwise this has failed
+
+	DEBUG_LOG_AIM(player->getName(), "Nothing suitable selected (%s)",
+	              item_curr
+	              ? item[weap_idx - WEAPONS].getName()
+	              : weap_curr
+	                ? weapon[weap_idx].getName() : "NOTHING")
+
+	return false;
+}
+
+
+/// @return false if the initialization of this instance failed
+bool AICore::can_work() const
+{
+	return canWork;
+}
+
+
+/** @brief check the currently set item list and update its organization
+  *
+  * This checks every item compared to the currently selected
+  * target and sets a score on usability. The list is then
+  * sorted by score in descending order.
+**/
+void AICore::checkItemMem()
+{
+	item_curr = item_head;
+
+	DEBUG_LOG_AIM(player->getName(), "Starting to check item memory", 0)
+
+	while (item_curr) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		// Update score
+		updateItemScore(item_curr);
+
+		// Advance
+		item_curr = item_curr->next;
+	}
+
+	// Eventually sort the list
+	sort_entries(&item_head);
+}
+
+
+/** @brief check the currently set memory and update its organization
+  *
+  * This looks into each entry whether there is new damage for
+  * this turn, and updates the score.
+  * After the score updates, the list is reordered, so the entry
+  * with the highest score becomes mem_head, and the list ends with
+  * the lowest scored entry.
+**/
+void AICore::checkOppMem()
+{
+	mem_curr = mem_head;
+
+	DEBUG_LOG_AIM(player->getName(), "Starting to check opponent memory", 0)
+
+	while (mem_curr) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		// Update score
+		updateOppScore(mem_curr);
+
+		// Advance
+		mem_curr = mem_curr->next;
+	}
+
+	// Do we have a new revengee?
+	if (revengee && (player->revenge != revengee->opponent))
+		player->revenge = revengee->opponent;
+
+	// Not a single one?
+	if (nullptr == revengee)
+		player->revenge = nullptr;
+
+	// Eventually sort the list
+	sort_entries(&mem_head);
+}
+
+
+/** @brief check the currently set weapon list and update its organization
+  *
+  * This checks every weapon compared to the currently selected
+  * target and sets a score on usability. The list is then
+  * sorted by score in descending order.
+**/
+void AICore::checkWeapMem()
+{
+	weap_curr = weap_head;
+
+	DEBUG_LOG_AIM(player->getName(), "Starting to check weapon memory", 0)
+
+	while (weap_curr) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		// Update score
+		updateWeapScore(weap_curr);
+
+		// Advance
+		weap_curr = weap_curr->next;
+	}
+
+	// Eventually sort the list
+	sort_entries(&weap_head);
+}
+
+
+/// @brief destroy all memory chains
+void AICore::destroy()
+{
+	while (item_head) {
+		item_curr = item_head;
+		item_head = item_curr->next;
+		delete item_curr;
+	}
+	item_curr = nullptr;
+	item_last = nullptr;
+
+	while (mem_head) {
+		mem_curr = mem_head;
+		mem_head = mem_curr->next;
+		delete mem_curr;
+	}
+	mem_curr = nullptr;
+	mem_last = nullptr;
+
+	while (weap_head) {
+		weap_curr = weap_head;
+		weap_head = weap_curr->next;
+		delete weap_curr;
+	}
+	weap_curr = nullptr;
+	weap_last = nullptr;
+}
+
+
+/// @brief Forbid AICore to create FLOATTEXT instances
+void AICore::forbidText()
+{
+	textAllowed.store(false);
+}
+
+
+/// @brief Get a string describing the given AI @a level
+const char* AICore::getLevelName  (int32_t level) const
+{
+	switch (level) {
+		case 0:
+			return "HUMAN (!!!)";
+			break;
+		case 1:
+			return "Useless";
+			break;
+		case 2:
+			return "Guesser";
+			break;
+		case 3:
+			return "Range Finder";
+			break;
+		case 4:
+			return "Targetter";
+			break;
+		case 5:
+			return "Deadly";
+			break;
+		case 6:
+			return "Deadly + 1";
+			break;
+		default:
+			break;
+	}
+	return "OUT OF RANGE (!!!)";
+}
+
+
+/** @brief get the set players memory and check it
+  *
+  * This method fetches all sOpponent entries from the handled player,
+  * and fills the item and weapon chains with the stock count and weapon
+  * preferences.
+  *
+  * The scores are not calculated, and the list is not sorted.
+  * Therefore checkOppMem() must be called first when the thread
+  * starts in operator().
+  *
+  * @return true if the memory could be copied
+**/
+bool AICore::getMemory()
+{
+	assert(player         && "ERROR: getMemory() reached with nullptr player?");
+	assert( tank          && "ERROR: getMemory() reached with nullptr tank?");
+	assert(!tank->destroy && "ERROR: getMemory() reached with destroyed tank?");
+
+
+	/// === 1) Copy item information ===
+
+	int32_t idx  = 0;
+	int32_t pref = 0;
+
+	item_curr = item_head;
+	item_last = nullptr;
+
+	assert(item_head && "ERROR: getMemory() called without item memory set up!");
+
+	if (!item_head)
+		return false;
+
+	do {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		if ( -1 < (pref = player->getItemPref(idx) ) ) {
+			item_curr->amount     = player->ni[idx];
+			item_curr->preference = pref;
+			item_curr->selectable = item[idx].selectable;
+			item_curr->type       = idx;
+
+			// The kamikaze value is only pre-set to true for vengeance
+			// items, all other must be determined if the bot really
+			// chooses to self-destruct.
+			if ( (item_curr->type >= ITEM_VENGEANCE)
+			  && (item_curr->type <= ITEM_FATAL_FURY) )
+				item_curr->kamikaze = true;
+			else
+				item_curr->kamikaze = false;
+
+			// Advance current
+			item_curr = item_curr->next;
+		}
+	} while ((++idx < ITEMS) && item_curr);
+
+
+	/// === 2) Copy opponents information ===
+
+	idx = 0;
+	int32_t    jcnt = 0; // Jedi count
+	int32_t    scnt = 0; // Sith count;
+	double     dail = ai_level_d; // [d]ouble [ai]_[l]evel
+	sOpponent* opp  = nullptr;
+
+	mem_curr = mem_head;
+	mem_last = nullptr;
+
+	assert(mem_head && "ERROR: getMemory() called without opponents memory set up!");
+
+	if (!mem_head)
+		return false;
+
+	do {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		mem_curr->alive = false; // must be confirmed
+
+		if ( (opp = player->getOppMem(idx) ) ) {
+			TANK* oppTank = nullptr;
+
+			mem_curr->attempts    = 0;
+			mem_curr->entry       = opp;
+			mem_curr->revengeDone = false;
+
+			if (  opp->opponent
+			  &&  opp->opponent->tank
+			  && !opp->opponent->tank->destroy)
+				oppTank = opp->opponent->tank;
+
+			// The other values depend on whether an active tank was found:
+			if (oppTank) {
+				mem_curr->is_buried  = oppTank->howBuried(&mem_curr->buried_l,
+				                                          &mem_curr->buried_r)
+				                       > BURIED_LEVEL ? true : false;
+				mem_curr->hasRepulse = oppTank->hasRepulsorActivated();
+				mem_curr->opLife     = oppTank->l + oppTank->sh;
+				mem_curr->opX        = oppTank->x;
+				mem_curr->opY        = oppTank->y;
+				mem_curr->diffLife   = currLife - mem_curr->opLife;
+				mem_curr->distance   = FABSDISTANCE2(x, y, mem_curr->opX, mem_curr->opY);
+
+				if (oppTank->l > 0)
+					mem_curr->alive = true;
+
+				// Is this the last one? then set as default best choice:
+				if (last_opp == opp)
+					best_setup_mem = mem_curr;
+			} else {
+				// Reset some values if there is no tank
+				mem_curr->opLife   = 0.;
+				mem_curr->diffLife = currLife;
+				mem_curr->distance = env.screenWidth * env.screenHeight;
+			}
+
+			// Some calculations can be cut short if this is ourselves:
+			if (opp->opponent == player) {
+				mem_curr->onSameTeam = true;
+				mem_curr->team_mod   = ( ( (player->painSensitivity  + 1.)   // [1;4]
+				                         * (player->selfPreservation + 1.) ) // [1;4]
+				                       + 2.) // [ 3  ; 18]
+				                     / -1.;  // [-1.5; -9]
+			} else {
+				mem_curr->onSameTeam = ( (TEAM_NEUTRAL   != player->team)
+				                      && (opp->opponent->team == player->team) );
+
+				// team_mod is a multiplier reflecting the general behaviour
+				// against the own and other teams.
+				if ( TEAM_JEDI == player->team ) {
+					// Jedi go strongly for Sith and protect their team
+					if (mem_curr->onSameTeam)
+						mem_curr->team_mod = (2. + dail) / -2.; // [-1.5; -4.]
+					else if ( TEAM_SITH == opp->opponent->team ) {
+						mem_curr->team_mod = 2. * ai_level;     // [2;12]
+					} else
+						mem_curr->team_mod = ai_level;          // [1; 6]
+				} else if ( TEAM_SITH == player->team ) {
+					// Sith go for everyone, slightly favouring Jedi and do
+					// not care that much hitting their own team members.
+					if (mem_curr->onSameTeam)
+						mem_curr->team_mod = (2. + dail) / -3.; // [-1; -2.66]
+					else if ( TEAM_JEDI == opp->opponent->team ) {
+						mem_curr->team_mod = 1.25 * ai_level;   // [1.25;7.5]
+					} else
+						mem_curr->team_mod = ai_level;          // [1   ;6  ]
+				} else {
+					// Neutrals go slightly more for the teams, and less for
+					// other neutrals. This is supposed to reflect the fact
+					// that Jedi and Sith have friends with them helping them
+					// out. Neutrals are all alone and considered less dangerous.
+					if ( TEAM_NEUTRAL == opp->opponent->team )
+						mem_curr->team_mod = 1. + (dail / 2.);  // => [1.5;4.]
+					else {
+						mem_curr->team_mod = .5 + dail;         // => [1.5;6.5]
+						if (TEAM_JEDI == opp->opponent->team)
+							++jcnt;
+						else
+							++scnt;
+					}
+				} // End of team_mod determination
+			} // End of opponent handling
+
+			// Advance current
+			mem_curr = mem_curr->next;
+		}
+		++idx;
+	} while (opp && mem_curr);
+
+	// If this is a neutral player, it has counted jedi and sith. This is
+	// done to raise the team_mod whenever any of these teams sport more
+	// than one remaining tank.
+	if ( (TEAM_NEUTRAL == player->team) && ( (jcnt > 1) || (scnt > 1) ) ) {
+		double j_mod = dail / 10. * static_cast<double>(jcnt - 1);
+		double s_mod = dail / 10. * static_cast<double>(scnt - 1);
+		mem_curr = mem_head;
+
+		while (mem_curr) {
+
+			if ( (TEAM_JEDI == mem_curr->entry->opponent->team)
+			  && (jcnt > 1) )
+				mem_curr->team_mod += j_mod;
+			else if ( (TEAM_SITH == mem_curr->entry->opponent->team)
+			       && (scnt > 1) )
+				mem_curr->team_mod += s_mod;
+
+			mem_curr = mem_curr->next;
+		}
+	}
+
+	/// === 3) Copy weapon information ===
+
+	double dmgMod  = player->damageMultiplier;
+	idx  = 0;
+	pref = 0;
+
+	weap_curr = weap_head;
+	weap_last = nullptr;
+
+	assert(weap_head && "ERROR: getMemory() called without weapon memory set up!");
+
+	if (!weap_head)
+		return false;
+
+	// Reset blast values, they have to be found anew:
+	blast_min = 0.;
+	blast_med = 0.;
+	blast_big = 0.;
+	blast_max = 0.;
+
+	do {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		if ( -1 < (pref = player->getWeapPref(idx) ) ) {
+			int32_t subMun = weapon[idx].submunition; // short-cut
+			double  damage = weapon[idx].damage * dmgMod
+			               * weapon[idx].getDelayDiv();
+
+			// === Dirt weapons have a "damage" based on their radius ===
+			if ( (DIRT_BALL         <= idx)
+			  && (SMALL_DIRT_SPREAD >= idx) )
+				damage = weapon[idx].radius * (1.5 + player->defensive);
+
+			weap_curr->amount     = player->nm[idx];
+
+			// To be able to track weapons that trigger other sub munitions,
+			// their configuration must be remembered, too:
+			weap_curr->subMunCount= weapon[idx].numSubmunitions;
+			weap_curr->subMunType = subMun;
+			// Non-spread weapons have their spread value set to 1. To catch
+			// future cases where this might be different, the value is
+			// stored and sanitized here:
+			weap_curr->spread     = weapon[idx].spread > 0 ? weapon[idx].spread : 1;
+			weap_curr->dmgCluster = weapon[idx].numSubmunitions > 0
+			                      ? dmgMod
+			                        * static_cast<double>(weapon[subMun].damage)
+			                        * static_cast<double>(weapon[idx].numSubmunitions)
+			                      : 0.;
+			weap_curr->dmgSingle  = damage;
+			weap_curr->dmgSpread  = damage
+			                      * static_cast<double>(weap_curr->spread);
+			weap_curr->preference = pref;
+			weap_curr->radius     = weapon[idx].radius;
+			weap_curr->type       = idx;
+
+			// Save blast value for opponents score calculation
+			double ds = weap_curr->dmgSingle;
+			if (SML_MIS == idx) {
+				if (blast_min < ds) blast_min = ds;
+				if (blast_med < ds) blast_med = ds;
+				if (blast_big < ds) blast_big = ds;
+				if (blast_max < ds) blast_max = ds;
+			} else if ( ( (MED_MIS == idx) || (LRG_MIS == idx) )
+			       && (weap_curr->amount > 0) ) {
+				if (blast_med < ds) blast_med = ds;
+				if (blast_big < ds) blast_big = ds;
+				if (blast_max < ds) blast_max = ds;
+			} else if ( ( (SML_NUKE == idx) || (NUKE == idx) )
+			       && (weap_curr->amount > 0) ) {
+				if (blast_big < ds) blast_big = ds;
+				if (blast_max < ds) blast_max = ds;
+			} else if ( ( (DTH_HEAD == idx) )
+			       && (weap_curr->amount > 0)
+			       && (blast_max < ds) )
+				blast_max = ds;
+
+			// If this is the last weapon used, store as default best choice
+			if (last_weap == idx)
+				best_setup_weap = weap_curr;
+
+			// Advance current
+			weap_curr = weap_curr->next;
+		}
+	} while ((++idx < WEAPONS) && weap_curr);
+
+	return true;
+}
+
+
+/// @brief flatten curr_ang if a riot bom is chosen to clear the path
+/// Note: No checks about being blocked, call it when appropriate. Only
+///       the weapon is checked against riot bombs.
+void AICore::flattenCurrAng()
+{
+	if ( (RIOT_BOMB     <= weap_curr->type)
+	  && (HVY_RIOT_BOMB >= weap_curr->type) ) {
+		int32_t div = 1 + ( (RAND_AI_1P + 2) / 2); // [2;4]
+		// Minimum : 1 + ( (0 + 2) / 2 ) = 1 + ( 2 / 2 ) = 1 + 1 = 2
+		// Useless:  1 + ( (1 + 2) / 2 ) = 1 + ( 3 / 2 ) = 1 + 1 = 2
+		// Deadly+1: 1 + ( (5 + 2) / 2 ) = 1 + ( 7 / 2 ) = 1 + 3 = 4
+		if (curr_angle > 180)
+			curr_angle += (270 - curr_angle) / div;
+		else
+			curr_angle -= (curr_angle - 90) / div;
+	}
+}
+
+
+/// @brief adapt @a ang_mod and @a pow_mod when a shot using them crashed.
+void AICore::fixCrashed (int32_t &ang_mod, int32_t &pow_mod)
+{
+	// Unless a hill was detected, the angle mod must not be greater than 1
+	if (!hill_detected && (ang_mod > 1))
+		ang_mod = 1;
+
+	// Use a unified angle or there has to be an if/else for the same code
+	int32_t fix_ang = curr_angle > 180 ? FLIP_ANGLE(curr_angle) : curr_angle;
+
+	// The following rules must be applied:
+	// 1) If boxed mode is on, the angle must not be higher than 150°
+	//    or it has to be reduced more.
+	// 2) Otherwise lower the angle further away from 45° (aka 135° here) if
+	//    it is already low.
+	//    If it isn't, it is raised anyway.
+	// 3) If none of the above apply, assume the angle to be in order if it
+	//    is between 130 and 140, which is 45° +/- 5°.
+	if (env.isBoxed && (fix_ang > 150)) {             // 1)
+		ang_mod *= -1 * RAND_AI_1P;
+		// Do not overdo it:
+		while (std::abs(ang_mod) > (ai_level + 2))
+			ang_mod /= 2;
+	} else if ( (fix_ang > 100) && (fix_ang <= 135) ) // 2)
+		ang_mod *= -1;
+	else if ( (fix_ang > 130) && (fix_ang <  140) )   // 3)
+		ang_mod = 0; // None needed
+
+	// now flip ang_mod if the angle was flipped:
+	ang_mod *= curr_angle > 180 ? -1 : 1;
+
+	// The power must be reduced if it is greater than the x distance.
+	int32_t pow_diff = curr_power - std::abs(mem_curr->opX - x);
+    if (pow_diff > 0) {
+		pow_mod = std::abs(pow_mod) * -1;
+		// And strengthen the power reduction more if
+		// the power is more than 50% over the distance
+		if ( pow_diff >= (std::abs(mem_curr->opX - x) / 2) )
+			pow_mod *= 2 * RAND_AI_1P;
+    }
+}
+
+
+/// @brief Try to adapt @a ang_mod and @a pow_mod according to the current
+/// overshoot and where the best hit landed.
+void AICore::fixOvershoot(int32_t& ang_mod, int32_t& pow_mod, int32_t hit_score)
+{
+	// Here are some more possible (sub) situations to consider:
+	// 1) The current score is at least better than the last.
+	//    This can happen if the shot does no longer hit team mates.
+	//    The important situation is, if the overshoot is very small
+	//    and a new best score is achieved. The bigger the weapon, the
+	//    higher the probability that this might be the case.
+	// 2) Both the current and the last overshoot were negative, the
+	//    angle was optimized towards 45° and the power was raised.
+	//    Having a worse overshoot then can happen if the gun was
+	//    lowered and the shot crashes into the side of a hill or
+	//    mountain.
+	//    The angle must then be brought towards 180° more than the
+	//    last angle modification brought it away from it.
+	// 3) The current score is worse than the last score.
+	//     a) The last score was better than the one before.
+	//        The modifications might have been too strong, try
+	//        values between the two.
+	//     b) That was two worse tries in a row.
+	//        The direction was wrong, and the last modifications
+	//        must be reverted and strengthened by the current set
+	//        mods.
+	// 4) No last score or the same, just adapt the mods according to
+	//    whether the shot was too short or too long.
+
+
+	bool angle_was_optimized = false;
+	if ( ( (curr_angle > 180) && (curr_angle <= 235) && (last_ang_mod > 0) )
+	  || ( (curr_angle < 180) && (curr_angle >= 135) && (last_ang_mod < 0) ) )
+		angle_was_optimized = true; // Optimized towards 45° on its side
+
+	// reset hill detection if the current overshoot isn't short
+	if ( (curr_overshoot > 0) || (hit_score > 0) )
+		hill_detected = false;
+
+	if (last_score && (hit_score > 0) && (hit_score > last_score)) {
+
+		// 1) Better score with worse overshoot.
+		// Here a best score might happen. But this is only noteworthy
+		// if the hit_score is positive. Otherwise it would simply
+		// mean that less damage was done (team and others) and that is
+		// hardly anything to note down.
+		// Note: if this is a new best score, some adaptation has
+		//       already been made in aim().
+		if (hit_score < best_score) {
+			// Only adapt the signedness of the mods and note that
+			// the aiming is not there, yet:
+			DEBUG_LOG_AIM(player->getName(),
+			              " => Better score %d [%d last]",
+			              hit_score, last_score)
+			ang_mod = std::abs(ang_mod) * SIGN(last_ang_mod);
+			pow_mod = std::abs(pow_mod) * SIGN(curr_overshoot) * -1;
+		}
+
+		// At least better than the last
+		last_was_better = true;
+		hill_detected   = false;
+	} else if ( (hit_score <= 0)
+	         && (last_overshoot < 0)
+	         && (curr_overshoot <= last_overshoot) // Keeps being too short
+	         && angle_was_optimized) {
+
+		// 2) Assume a hill in the path
+		pow_mod = (std::abs(pow_mod) + std::abs(last_pow_mod)) / 2; // raise it
+		ang_mod = static_cast<double>(std::abs(last_ang_mod) + std::abs(ang_mod))
+		        * ai_over_mod * SIGNd(last_ang_mod) * -1.;
+		// Note: This accumulates the last and the current angle modification,
+		//       strengthens depending on AI level and ensures it has the
+		//       opposite direction from the last modification.
+
+		// Make sure the new ang_mod really gets the angle upwards:
+		if ( SIGN(curr_angle - 180) == SIGN(ang_mod) )
+			ang_mod *= -1;
+
+		// Make sure the new ang_mod doesn't make the angle to flip over:
+		if ( (curr_angle > 180) && ((curr_angle + ang_mod) <= 180) )
+			ang_mod = 181 - curr_angle;
+		if ( (curr_angle < 180) && ((curr_angle + ang_mod) >= 180) )
+			ang_mod = curr_angle - 181;
+
+		DEBUG_LOG_AIM(player->getName(),
+		              "Assuming hill crash, reverting ang_mod to %d",
+		              ang_mod)
+
+		last_was_better = false; // false, so this change won't get directly
+		// reverted again.
+		hill_detected   = true;
+	} else if (last_score && (last_score > hit_score) ) {
+		// 3) Wrong direction!
+		if (last_was_better) {
+
+			DEBUG_LOG_AIM(player->getName(),
+			              " => Worse score %d [%d was better]",
+			              hit_score, last_score)
+
+			// Try a mod in between the two
+			if (std::abs(last_ang_mod) > 1)
+				ang_mod = -1 * (last_ang_mod / 2);
+			else
+				ang_mod = -1 * SIGN(last_ang_mod);
+			if (std::abs(last_pow_mod) > 9)
+				pow_mod = -1 * (last_pow_mod / 2);
+			else
+				pow_mod = -5 * SIGN(last_pow_mod);
+		} else {
+			// b) Revert and go in the opposite direction:
+
+			DEBUG_LOG_AIM(player->getName(),
+			              " => Worse score %d [%d was worse]",
+			              hit_score, last_score)
+
+			// If the last was reverted already, strengthen the
+			// move in the opposite direction
+			if (last_reverted) {
+				// First make positive and strengthen
+				ang_mod = std::abs(ang_mod) * (RAND_AI_0P + 1);
+				pow_mod = std::abs(pow_mod) * (RAND_AI_1P + 1);
+			}
+
+			// Then strengthen the last values by the current and revert:
+			ang_mod = -1 * (  last_ang_mod
+			                + (SIGN(last_ang_mod) * ang_mod) );
+			pow_mod = -1 * (  last_pow_mod
+			                + (SIGN(last_pow_mod) * pow_mod) );
+
+			// Adapt by overshoot: (Yes, still necessary)
+			if (SIGN(curr_overshoot) == SIGN(pow_mod))
+				pow_mod *= -1;
+		}
+
+		last_was_better = false;
+	} else {
+		// 4) Just do the set mod according to overshoot
+
+		DEBUG_LOG_AIM(player->getName(), "=> Same score %d, overshoot %d",
+		              hit_score, curr_overshoot)
+
+		// Put in some limits for the angle according to where the opponent is
+		int32_t ang_limit = (focusRate * static_cast<double>(rand() % 16));
+		int32_t dist_y    = mem_curr->opY - y;
+		if      (dist_y < -100) /* above */ ang_limit += 60;
+		else if (dist_y >  100) /* below */ ang_limit += 20;
+		else                    /* equal */ ang_limit += 40;
+
+		// If a hill was detected, do not modify angle more than 1
+		if (hill_detected && (ang_mod > 1))
+			ang_mod = 1;
+
+		if ( ( (curr_angle <= 180) && (curr_angle > (90 + ang_limit)) )
+		  || ( (curr_angle > (270 - ang_limit) ) ) )
+			ang_mod *= -1;
+
+		// Adapt pow_mod by overshoot
+		pow_mod = std::abs(pow_mod) * SIGN(curr_overshoot) * -1;
+
+		last_was_better = false;
+	}
+}
+
+
+/// @brief adapt @a ang_mod and @a pow_mod when a shot using them did not
+/// finish.
+void AICore::fixUnfinished (int32_t &ang_mod, int32_t &pow_mod)
+{
+	// Put in some limits for the angle according to where the opponent is
+	int32_t ang_limit = (focusRate * static_cast<double>(rand() % 16));
+	int32_t dist_y    = mem_curr->opY - y;
+	if      (dist_y < -100) /* above */ ang_limit += 60;
+	else if (dist_y >  100) /* below */ ang_limit += 20;
+	else                    /* equal */ ang_limit += 40;
+
+	// If a hill was detected on the path, do not alter the angle
+	// more than by 1
+	if (hill_detected && (ang_mod > 1))
+		ang_mod = 1;
+
+	// If the angle is too steep to the right, or too flat
+	// to the left, make ang_mod negative:
+	// Note: If the shot is to the right, it is in the range
+	// 90-180 and going down needs a negative mod.
+	// If going to the left it needs a positive mod to go
+	// down as it is in the range 180 - 270.
+
+	if ( ( (curr_angle <= 180) && (curr_angle > (90 + ang_limit)) )
+	  || ( (curr_angle > (270 - ang_limit) ) ) )
+		ang_mod *= -1;
+
+	// The power must be reduced if it is greater than twice the
+	// x distance, but raised if less than the simple x distance.
+	// If the power is too low, shots can quickly end up with too
+	// many bounces if the wall is rubber or spring.
+    int32_t dist_x = std::abs(mem_curr->opX - x);
+    if (curr_power > (2 * dist_x))
+		pow_mod *= -1;
+	else if (curr_power > dist_x)
+		pow_mod = 0; // Do not change
+}
+
+
+/// @return true once the operator() ends
+bool AICore::hasExited() const
+{
+	return isFinished;
+}
+
+
+/// @brief initialize work with the current players data
+bool AICore::initialize()
+{
+	DEBUG_LOG_AI(player->getName(), "Starting think work, setting up.", 0)
+
+	/// === Step 1 : Copy relevant data ===
+	ai_level      = static_cast<int32_t>(player->type);
+	ai_level_d    = static_cast<double>(ai_level);
+	ai_over_mod   = 1. + (ai_level_d / 10.); // [1.1;1.5]
+	ai_type_mod   = (1. + ai_level_d) / 2.;  // [1.0;3.0]
+	blast_min     = 0.;
+	blast_med     = 0.;
+	blast_big     = 0.;
+	blast_max     = 0.;
+	isShocked     = false;
+	revengee      = nullptr;
+	shocker       = nullptr;
+	needSuccess   = true;
+	needAim       = true;
+	isBlocked     = false;
+	hill_detected = false;
+
+	// Data from player:
+	needMoney = ((player->getMoneyToSave(false) - player->money) > 0);
+	last_opp  = player->getOppMem(-1);
+
+	// Data from tank:
+	tank      = player->tank;
+	if (tank && !tank->destroy) {
+		angle     = tank->a;
+		power     = tank->p;
+		weap_idx  = tank->cw;
+		buried    = tank->howBuried(&buried_l, &buried_r);
+		currLife  = tank->l + tank->sh;
+		maxLife   = tank->getMaxLife();
+		x         = tank->x;
+		y         = tank->y;
+		last_ang  = 180;
+		last_pow  = 1000;
+		last_weap = 0;
+
+		// Is there a last opponent?
+		if (last_opp) {
+			last_ang  = angle;
+			last_pow  = power;
+			last_weap = weap_idx;
+			DEBUG_LOG_AI(player->getName(), "Last opponent was %s",
+						 last_opp->opponent->getName())
+		}
+
+		// Select last weapon/item used
+		if (weap_idx > WEAPONS)
+			useItem(weap_idx);
+		else
+			useWeapon(weap_idx);
+	} else
+		return false;
+
+	// reset calculation values
+	curr_angle     = angle;
+	curr_power     = power;
+
+	// Reset setup values:
+	best_round_score     = NEUTRAL_ROUND_SCORE;
+	best_setup_angle     = angle;
+	best_setup_item      = nullptr;
+	best_setup_mem       = nullptr;
+	best_setup_overshoot = MAX_OVERSHOOT;
+	best_setup_power     = power;
+	best_setup_score     = NEUTRAL_ROUND_SCORE;
+	best_setup_weap      = nullptr;
+
+
+	/// === Step 2: See whether this bot gets lucky ===
+	if ((rand() % 100) < ai_level) {
+		// So the useless bot has a 1% and the deadly bot a 5% chance
+		int32_t raise = 1 + ( (5 - ai_level) / 2);
+		/* Useless: 1 + ((5 - 1) / 2) =>  1 + (4 / 2) => 1 + 2 => 3
+		 * Guesser: 1 + ((5 - 2) / 2) =>  1 + (3 / 2) => 1 + 1 => 2
+		 * Ranger : 1 + ((5 - 3) / 2) =>  1 + (2 / 2) => 1 + 1 => 2
+		 * Target : 1 + ((5 - 4) / 2) =>  1 + (1 / 2) => 1 + 0 => 1
+		 * Deadly : 1 + ((5 - 5) / 2) =>  1 + (0 / 2) => 1 + 0 => 1
+		 */
+		DEBUG_LOG_AI(player->getName(),
+					  "Lucky Turn: Raise from \"%s\" to \"%s\"",
+					  getLevelName(ai_level),
+					  getLevelName(ai_level + raise))
+		ai_level     += raise;
+		ai_level_d    = static_cast<double>(ai_level);
+		ai_over_mod   = 1. + (ai_level_d / 10.); // [1.1;1.5]
+		ai_type_mod   = (1. + ai_level_d) / 2.;  // [1.0;3.0]
+		showFeedback("*lucky*", GREEN, -.8, TS_NO_SWAY, 100);
+	}
+
+
+	/// === Step 3 : Set stage and allow the work to be done ===
+	if (tank && !tank->destroy && !isStopped && canWork && getMemory()) {
+		// Note: Without the memory, no real work is possible.
+
+		textAllowed.store(true);
+		plStage     = PS_SELECT_TARGET;
+		isWorking   = true;
+		return true;
+	}
+
+	return false;
+}
+
+
+/// @brief Sanitize curr_angle and curr_power.
+void AICore::sanitizeCurr()
+{
+	if (curr_angle <        90) curr_angle =        90;
+	if (curr_angle >       270) curr_angle =       270;
+	if (curr_power > MAX_POWER) curr_power = MAX_POWER;
+	if (curr_power < MIN_POWER) curr_power = MIN_POWER;
+	curr_power      -= curr_power % 5;
+}
+
+
+/// @brief show ai feedback if allowed and not skipping computer play.
+/// Whenever a feedback message is shown, the AI sleeps for dur/10 + 1 ms.
+void AICore::showFeedback(const char* const feedback, int32_t col, double yv,
+                          eTextSway text_sway, int32_t dur)
+{
+	if (env.showAIFeedback && !global.skippingComputerPlay) {
+		// Wait for the AI to be allowed to create texts
+		while (!textAllowed.load(ATOMIC_READ))
+			std::this_thread::yield();
+
+		int32_t y_pos = y - (50 + (rand() % 21));
+		new FLOATTEXT(feedback, x, y_pos, .0, yv, col, CENTRE, text_sway, dur);
+		MSLEEP( (dur / 10) + 1 )
+	}
+}
+
+
+/** @brief Select the next item to use on the current target.
+  *
+  * This method tries to determine the best item / weapon selection
+  * to be used on the currently selected target (mem_curr).
+  *
+  * Some of the thinking depend on random numbers, so calling this
+  * method twice on the same target might lead to different selections.
+  *
+  * This is wanted, so many tries on higher ai levels with a small
+  * number of difficult to reach targets might eventually lead to
+  * a sane result.
+  *
+  * The selected item / weapon is saved in item_curr or weap_curr. The
+  * method makes sure that it is not the same as item_last / weap_last.
+  * Please note, however, that if there is only one selectable item,
+  * if the bot is out of stock of everything but small missiles for example,
+  * then item_curr / weap_curr might end up the same as the last selections.
+  *
+  * The method returns true if the selection it ends up with makes sense.
+  * If @a is_last is set to true, the method itself returns true, too.
+  *
+  * @param[in] is_last If set to true, the method will return true in any case.
+  * @return true if the selection makes sense, or if @a is_last is set to true.
+**/
+bool AICore::selectItem(bool is_last)
+{
+	// Back up current selections
+	item_last = item_curr;
+	weap_last = weap_curr;
+
+	// Advance to the next weapon
+	bool has_weap = true;
+
+	// If a best setup with primary target hit has been achieved
+	// already, or if the bot is shocked, select a random weapon.
+	// Otherwise do an ordered advance down the chain.
+	if (best_setup_prime || isShocked) {
+		int32_t weap_num = rand() % WEAPONS;
+		weap_curr = weap_head;
+
+		// If weap_last was not set, set it to head, too.
+		if (!weap_last)
+			weap_last = weap_curr;
+
+		// Now rotate until the weapon is found.
+		while (weap_num) {
+			weap_curr = weap_curr->next;
+
+			// Skip not available weapons, non-damage entries, the last weapon
+			// and weapons with a negative score.
+			while ( weap_curr
+			     && ( (weap_curr->amount <= 0)
+			       || (weap_curr->dmgSingle < 2.)
+			       || (weap_curr->score <= 0)
+			       || (weap_curr == weap_last)
+			       // Reducer and dirt weapons are non-damage, too.
+			       // they have a fake damage set, so filter them here.
+			       || (REDUCER == weap_curr->type)
+			       || ( (DIRT_BALL <= weap_curr->type)
+			         && (SMALL_DIRT_SPREAD >= weap_curr->type) ) ) )
+				weap_curr = weap_curr->next;
+
+			// Rotate if the end was hit
+			if (!weap_curr)
+				weap_curr = weap_head;
+
+			--weap_num;
+		}
+	} else {
+		while ( has_weap
+		    && ( !weap_curr
+		      || (weap_curr == weap_last)
+		      || (weap_curr->amount <= 0)
+		      || (weap_curr->dmgSingle < 2.)
+		      || (weap_curr->score <= 0)
+		      || ( mem_curr->hasRepulse
+		        && RAND_AI_1P
+		        && (SML_NAPALM <= weap_curr->type)
+		        && (LRG_NAPALM >= weap_curr->type)
+		        && RAND_AI_1P ) ) ) {
+			weap_curr = weap_curr ? weap_curr->next : weap_head;
+
+			// If no weapon was selected at the start, weap_last is
+			// now nullptr, but must be weap_head once weap_curr is
+			// beyond head.
+			if (!weap_last && (weap_curr != weap_head))
+				weap_last = weap_head;
+
+			// If this rotated once through everything, there is
+			// only this one weapon left or a lot of tries are through:
+			if (weap_last == weap_curr)
+				has_weap = false;
+		}
+	} // end of ordered rotation
+
+	// Use weap_head if there is no other weapon:
+	if (!has_weap) {
+		weap_curr = weap_head;
+		weap_last = weap_curr;
+	}
+
+	// If the bot is shocked, the next weapon is selected,
+	// someone in panic does not do much thinking any more
+	if (isShocked) {
+		item_curr = nullptr;
+		if (nullptr == weap_curr)
+			weap_curr = weap_head;
+
+		weap_idx = weap_curr->type;
+
+		DEBUG_LOG_EMO(player->getName(), "(SHOCKED) Quick selected %s",
+		              weapon[weap_idx].getName())
+		return true;
+	}
+
+	// Advance to the next item
+	bool has_item = true;
+	while ( has_item
+	    && ( !item_curr
+	      || (item_curr == item_last)
+	      || (item_curr->amount <= 0)
+	      || !item_curr->selectable
+	      || (item_curr->score <= 0) ) ) {
+		item_curr = item_curr ? item_curr->next : item_head;
+
+		// If no item was selected at the start, item_last is
+		// now nullptr, but must be item_head once item_curr is
+		// beyond head.
+		if (!item_last && (item_curr != item_head))
+			item_last = item_head;
+
+		// If this rotated once through everything, there is
+		// only this one weapon left:
+		if (item_last == item_curr)
+			has_item = false;
+	}
+
+	// If no items are available, it has to be taken out of consideration:
+	if (!has_item) {
+		item_curr = nullptr;
+		item_last = nullptr;
+	}
+
+
+	// Do not use items with a negative score, as those are items
+	// that are unavailable.
+	if (item_curr
+	  && ( (item_curr->score < 0)
+	    || (player->ni[item_curr->type] <= 0) ) )
+		item_curr = nullptr;
+
+	// Note: sub-optimal weapons ( too low damage ) can be negative.
+
+	// Do not use self destruct items/weapons unless the
+	// bot wants to self destruct
+	if (mem_curr->opLife <= (currLife * 10.)) {
+		if (item_curr && (item_curr->kamikaze))
+			item_curr = nullptr;
+		if (weap_curr && (weap_curr->kamikaze))
+			weap_curr = nullptr;
+	}
+
+	// Do not use teleporters unless buried or blocked
+	if ( item_curr
+	  && (item_curr->type >= ITEM_TELEPORT)
+	  && (item_curr->type <= ITEM_MASS_TELEPORT)
+	  && !isBlocked
+	  && !item_curr->escape)
+		item_curr = nullptr;
+
+	// Do not use riot bombs if the path is not blocked
+	if ( weap_curr
+	  && (weap_curr->type >= RIOT_BOMB)
+	  && (weap_curr->type <= HVY_RIOT_BOMB)
+	  && !isBlocked)
+		weap_curr = nullptr;
+
+	// If both are still set, take what has the higher score
+	if (item_curr && weap_curr) {
+        if (item_curr->score > weap_curr->score) {
+			weap_curr = nullptr;
+			weap_idx  = item_curr->type + WEAPONS;
+		} else {
+			item_curr = nullptr;
+			weap_idx  = weap_curr->type;
+		}
+	} else if (item_curr)
+		weap_idx = item_curr->type + WEAPONS;
+	else if (weap_curr)
+		weap_idx = weap_curr->type;
+	else
+		weap_idx = -1;
+
+	if (!item_curr && !weap_curr && is_last)
+		// If nothing is set but this is the last chance,
+		// use the small missile as a fallback weapon
+		useWeapon(SML_MIS);
+
+
+	DEBUG_LOG_EMO(player->getName(), "Next selection: %s",
+	              weap_curr
+	              ? weapon[weap_idx].getName()
+	              : item_curr
+	                ? item[weap_idx - WEAPONS].getName()
+	                : "NOTHING (fail)")
+
+	return (item_curr || weap_curr);
+}
+
+
+/** @brief Select the next target to try  to hit or handle.
+  *
+  * This method selects and sets the current target. Basically it
+  * just wanders down the memory chain as it is sorted by score already.
+  *
+  * The following additional rules (besides the ordering by score) apply:
+  *   - If the bot is shocked, the shocker is always selected.
+  *   - If a revenge is sought, that opponent is always selected if it
+  *     is not currently selected or was the last one.
+  *   - If @a is_last is set to true, the target with the highest score
+  *     or that is sought revenge against is selected and the method
+  *     returns true.
+  *
+  * Whenever the selection makes sense, the method returns true.
+  *
+  * @param[in] is_last If set to true, the method will return true in any case.
+  * @return true if the selection makes sense, or if @a is_last is set to true.
+**/
+bool AICore::selectTarget(bool is_last)
+{
+	// Be quickly done if this bot is shocked
+	if (isShocked) {
+
+		// Is the shocker still there?
+		if (shocker->opponent->tank && !shocker->opponent->tank->destroy) {
+			mem_last = mem_curr;
+			if (!mem_curr || (mem_curr->entry != shocker)) {
+				mem_curr = mem_head;
+				while (mem_curr && (mem_curr->entry != shocker))
+					mem_curr = mem_curr->next;
+			}
+
+			DEBUG_LOG_EMO(player->getName(), "(SHOCKED) Targetting %s",
+						  mem_curr ? mem_curr->entry->opponent->getName() : "NONE?")
+
+			return (mem_curr->entry == shocker);
+		} else {
+			// Nope, gone with the wind.
+			shocker   = nullptr;
+			isShocked = false;
+		}
+	}
+
+	// Preselect the revengee if not done already and there is one:
+	// Note: Of course the revengee is forced if this is the very last try!
+	if ( revengee
+	  && ( is_last
+		|| ( (!mem_curr || (mem_curr->entry != revengee))
+		  && (!mem_last || (mem_last->entry != revengee)) ) ) ) {
+
+		// is the revengee still alive?
+		if (revengee->opponent->tank && !revengee->opponent->tank->destroy) {
+			mem_last = mem_curr;
+			mem_curr = mem_head;
+			while (mem_curr && (mem_curr->entry != revengee))
+				mem_curr = mem_curr->next;
+
+			DEBUG_LOG_EMO(player->getName(), "(REVENGE) Targetting %s",
+						  mem_curr ? mem_curr->entry->opponent->getName() : "NONE?")
+
+			return (mem_curr->entry == revengee);
+		} else
+			// No longer relevant...
+			revengee = nullptr;
+	}
+
+	// If nothing was preselected, a simple walk down the chain
+	// is in order. (revengees must be skipped, though)
+	// However, if this is the last_try, the primary target is always selected.
+	sOppMemEntry* mem_old = mem_curr; // backup
+
+	if (is_last || (nullptr == mem_curr))
+		mem_curr = mem_head;
+
+	// If the revengee is currently selected, the "walk" must continue
+	// from the last opponent on, or the bot will have a flip between
+	// the revengee and the first other opponent only.
+	if (!is_last && revengee && mem_curr && (mem_curr->entry == revengee)) {
+		if (mem_last)
+			mem_curr = mem_last;
+		else
+			mem_curr = mem_head;
+	}
+
+	// Now walk down the list skipping the revengee if set
+	if (!is_last) {
+		while ( mem_curr
+		    && ( (mem_curr == mem_old)
+		      || (mem_curr == mem_last)
+		      || (revengee && (mem_curr->entry == revengee))
+		      || (mem_curr->entry->opponent == player)
+		      || (nullptr == mem_curr->entry->opponent->tank)
+		      || mem_curr->entry->opponent->tank->destroy) )
+			mem_curr = mem_curr->next;
+		mem_last = mem_old;
+	}
+
+	// If is_last is set, mem_curr must not be nullptr. Otherwise a
+	// nullptr can happen if too few tanks are left.
+	assert ( (!is_last || mem_curr) && "ERROR: Is last but nullptr curr!");
+
+	DEBUG_LOG_EMO(player->getName(), "( normal) Targetting %s",
+				  mem_curr ? mem_curr->entry->opponent->getName() : "nobody")
+
+	return (nullptr != mem_curr);
+}
+
+
+/** @brief setup the basic attack values
+  *
+  * This method tries to find a sane target-weapon-combination.
+  * The number of tries to do so is dictated by the AI level,
+  * and if this is the very last targeting try, the method will
+  * come up with the minimum possible combination.
+  *
+  * @param[in] is_last If set to true, something usable is forced to be set.
+  * @return true if a viable combination was found, false otherwise.
+**/
+bool AICore::setupAttack(bool is_last, int32_t &opp_attempt, int32_t &weap_attempt)
+{
+	bool selectDone     = false;
+	bool breakUp        = false;
+	bool has_new_target = false;
+
+	while (isWorking && !isStopped && !selectDone && !breakUp) {
+
+		// Yield on each iteration to not hog the CPUs
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		// 1) Select a target
+		//====================
+		if (!weap_attempt || !mem_curr) {
+			plStage = PS_SELECT_TARGET;
+
+			DEBUG_LOG_AIM(player->getName(), "Selecting target, try %d / %d",
+						  opp_attempt + 1, findOppAttempts)
+
+			selectDone = selectTarget( (++opp_attempt == findOppAttempts)
+			                        && is_last && needSuccess);
+			if (selectDone && (mem_curr != mem_last)) {
+				best_round_score = NEUTRAL_ROUND_SCORE; // New target!
+				item_curr = nullptr;
+				item_last = nullptr;
+				weap_curr = nullptr;
+				weap_last = nullptr;
+				has_new_target = true;
+			}
+		} else {
+			has_new_target = false;
+			selectDone = true;
+		}
+
+		// 2) Select an item / weapon
+		//============================
+		if (selectDone) {
+			plStage = PS_SELECT_WEAPON;
+
+			/* --- Ensure dedicated lists and starting values --- */
+			if (has_new_target) {
+				checkWeapMem();
+				checkItemMem();
+				weap_idx  = -1;
+			}
+
+			/* --- Now do the selection --- */
+			selectDone = false;
+			while ( isWorking && !isStopped && !selectDone
+			    && (weap_attempt < findWeapAttempts) ) {
+				DEBUG_LOG_AIM(player->getName(), "Selecting item, try %d / %d",
+							  weap_attempt + 1, findWeapAttempts)
+
+				// Yield on each iteration to not hog the CPUs
+				if (!global.skippingComputerPlay)
+					std::this_thread::yield();
+
+				selectDone = selectItem( (++weap_attempt == findWeapAttempts)
+				                      && is_last && needSuccess);
+			}
+
+			// selectItem() must have set weap_idx properly:
+			assert( ( !selectDone
+			       || ( item_curr && (weap_idx == WEAPONS + item_curr->type) )
+			       || ( weap_curr && (weap_idx ==           weap_curr->type) ) )
+			     && "ERROR: Selection done but weap_idx does not"
+			     && " contain the correct index!");
+
+			// If this was the last attempt to select a weapon, and another
+			// opponent selection is in order, reset weap_attempt so a new
+			// opponent can be selected
+			if ( (weap_attempt == findWeapAttempts)
+			  && (opp_attempt  <  findOppAttempts) )
+				weap_attempt = 0;
+		} // end of item selection
+
+		// break up this round if no selection was done but the
+		// opponent selection has ended
+		if (!selectDone
+		  && (opp_attempt  >= findOppAttempts)
+		  && (weap_attempt >= findWeapAttempts) )
+			breakUp = true;
+	} // end of basic setup cycle
+
+	// If the selection was not done properly (not possible) but this
+	// is the absolutely final round and there has been no good setup, yet,
+	// an emergency plan is used:
+	if (isWorking && !isStopped && is_last && !selectDone && needSuccess) {
+		// Try to "get out" first:
+		selectDone = useItem(ITEM_TELEPORT);
+
+		if (!selectDone && !mem_curr->is_buried)
+			selectDone = useItem(ITEM_SWAPPER);
+
+		if (!selectDone)
+			selectDone = useItem(ITEM_MASS_TELEPORT);
+
+		// If this still isn't going anywhere, revert to first target with
+		// small missiles, that might be useless now, but there is always
+		// a next round.
+		if (!selectDone) {
+			item_curr  = nullptr;
+			mem_curr   = mem_head;
+			selectDone = useWeapon(SML_MIS);
+
+			DEBUG_LOG_EMO(player->getName(),
+			              "Last Try Selection: %s against %s",
+			              weapon[SML_MIS].getName(),
+			              mem_head->entry->opponent->getName())
+
+			assert(selectDone && "ERROR: Not even small missile can be selected?");
+		} else
+			weap_curr = nullptr; // Emergency plan working.
+	}
+
+
+	// The last thing to consider is kamikaze.
+	// While the appropriate items *are* self destruct devices, choosing
+	// a weapon with kamikaze potential (and score) does not mean that the
+	// bot *must* destroy themselves. So in that case an extra test is in order.
+	if ( selectDone
+	  && ( (item_curr && item_curr->kamikaze)
+	    || (weap_curr && weap_curr->kamikaze) ) ) {
+		bool self_destruct = true;
+
+		// selfPreservation is a value in the interval [0;3]
+		if (weap_curr && ( (rand() % 35) < (player->selfPreservation * 10.)))
+			self_destruct = false;
+
+		// Another "way out" is if a weapon shall be used but it does
+		// not do enough damage:
+		if (self_destruct && weap_curr && (weap_curr->dmgSingle < currLife))
+			self_destruct = false;
+
+		// do not self destruct if a good best setup was already found
+		if (!needSuccess && best_setup_prime && (best_setup_score > 0))
+			self_destruct = false;
+
+		// If the bot still wants to self destruct, change mem_curr
+		// to reflect this:
+        if (self_destruct && (mem_curr->entry->opponent != player) ) {
+			mem_last = mem_curr;
+			mem_curr = mem_head;
+			while (mem_curr->entry->opponent != player)
+				mem_curr = mem_curr->next;
+
+			// This must never fail:
+			assert(mem_curr && "ERROR: Self not found in memory?");
+
+			DEBUG_LOG_EMO(player->getName(),
+			              "Chosen to self destruct using %s",
+						  weap_curr
+						  ? weapon[weap_idx].getName()
+						  : item_curr
+							? item[weap_idx - WEAPONS].getName()
+							: "NOTHING (fail)")
+
+        } else {
+			// To chicken out, the small missile is chosen if this is
+			// the last thing to consider
+			if (is_last && needSuccess)
+				useWeapon(SML_MIS);
+			else
+				selectDone = false;
+        }
+	}
+
+
+	// If both attempts, selecting an opponent and a weapon, have reached
+	// the maximum tries, they get reset, so the next full targeting cycle
+	// is triggered:
+	if ( !is_last && breakUp) {
+		opp_attempt  = 0;
+		weap_attempt = 0;
+	}
+
+	return selectDone;
+}
+
+
+/** @brief start the work on one player
+  *
+  * This method starts working on a new player. All relevant
+  * data is fetched from @a player_ that must not be nullptr.
+  *
+  * If a job is running, no new one is started.
+  * If @a player_ is not an AI player, it is not handled.
+  *
+  * @param[in] player Pointer to the player to handle.
+  * @return true if a job was started, false otherwise.
+  */
+bool AICore::start(PLAYER* player_)
+{
+	if ( canWork && player_ && !isWorking && !isStopped
+	  && (player_->type > HUMAN_PLAYER)
+	  && (player_->type < NETWORK_CLIENT)
+	  && (PS_AI_IS_IDLE == plStage) ) {
+
+		DEBUG_LOG_AI(player_->getName(), "==============================", 0)
+		DEBUG_LOG_AI(player_->getName(), " AICore started for %s", player_->getName())
+		DEBUG_LOG_AI(player_->getName(), "------------------------------", 0)
+
+		lguard_t guard(actionMutex);
+		plStage   = PS_AI_INITIALIZE;
+		player    = player_;
+		isWorking = true;
+		actionCondition.notify_one();
+
+		return true;
+	}
+
+	return false;
+}
+
+
+/** @brief Retrieve the current status of the AI
+  *
+  * The arguments will not be changed if the AI is not working
+  * on any player.
+  *
+  * @param[out] aItem Receives the currently selected weapon/item
+  * @param[out] aAngle Receives the currently set angle
+  * @param[out] aPower Receives the currently set power
+  * @param[out] pl_stage Receives the current stage of the AI. This is always sent.
+  * @return true if the AI is still working, false if it has finished.
+  */
+bool AICore::status(int32_t &aItem, int32_t &aAngle,
+					int32_t &aPower, ePlayerStages &pl_stage)
+{
+	pl_stage = plStage;
+
+	if (isWorking) {
+		aItem  = weap_idx;
+		aAngle = angle;
+		aPower = power;
+		return true;
+	}
+
+	return false;
+}
+
+
+/// @brief Tell the thread to stop even if it is not finished.
+void AICore::stop()
+{
+	lguard_t guard(actionMutex);
+	isStopped = true;
+	actionCondition.notify_one();
+}
+
+
+/** @brief Trace the sub munition of a cluster type weapon
+  *
+  * Damage is recorded in the opponent memory dmgDone value.
+  *
+  * @param[in] subType The type of the sub munition. No checks done!
+  * @param[in] subCount The number of sub munition parts.
+  * @param[in] sub_x Trigger x coordinate.
+  * @param[in] sub_y Trigger y coordinate.
+  * @param[in] inh_xv Parent missile xv the moment it triggered.
+  * @param[in] inh_yv Parent missile yv the moment it triggered.
+**/
+void AICore::traceCluster(int32_t subType, int32_t subCount,
+                          int32_t sub_x, int32_t sub_y,
+                          double inh_xv, double inh_yv)
+{
+	double    divergence   = weapon[weap_curr->type].divergence;
+	double    speedVar     = weapon[weap_curr->type].speedVariation;
+	double    spreadVar    = weapon[weap_curr->type].spreadVariation;
+	WEAPON*   sub_weap     = &weapon[subType];
+	double    divStep      = static_cast<double>(divergence)
+	                       / static_cast<double>(subCount - 1);
+	int32_t   startPoint   = divStep < 0. ? 0 : 180;
+	int32_t   randStart    = rand () % 1000000;
+	ePhysType subPhys      = PT_NORMAL;
+	int32_t   startY       = sub_y - 20;
+	int32_t   cl_overshoot = MAX_OVERSHOOT;
+	int32_t   old_overshoot= curr_overshoot; // overshoot is only used for mirvs and funkies
+	double    radius       = sub_weap->radius;
+	double    sub_dmg      = sub_weap->damage * player->damageMultiplier;
+
+	// If the weapon is fired into a ceiling, adapt starting y
+	if (env.isBoxed
+	  && (startY <= BOXED_TOP)
+	  && ( (WALL_STEEL == env.current_wallType)
+		|| ( (WALL_WRAP  == env.current_wallType)
+		  && (!env.isBoxed || !env.do_box_wrap) ) ) )
+		startY = MENUHEIGHT + 20;
+
+	// Change physics of the sub munitions for the funky bomb
+	if ( (weap_curr->type == FUNKY_BOMB) || (weap_curr->type == FUNKY_DEATH) )
+		subPhys = PT_FUNKY_FLOAT;
+
+	// If this is a steel wall hit, the start point angle needs
+	// to be adapted. And erased if this is a ceiling hit
+	if (WALL_STEEL == env.current_wallType) {
+		if ( (CLUSTER <= weap_curr->type) && (SUP_CLUSTER >= weap_curr->type) ) {
+			if ( x < 2 )
+				startPoint -= divergence + 1 + (rand() % 10);
+			else if ( x > (env.screenWidth - 3) )
+				startPoint += divergence + 1 + (rand() % 10);
+			else if ( y <= BOXED_TOP)
+				startPoint = 0;
+		} else if ( (SML_NAPALM <= weap_curr->type)
+				 && (LRG_NAPALM >= weap_curr->type) ) {
+			if ( x < 2 )
+				startPoint -= 10 + rand() % 21;
+			else if ( x > (env.screenWidth - 3) )
+				startPoint += 10 + rand() % 21;
+			else if ( y <= BOXED_TOP)
+				startPoint = 0;
+		} else if ( ( (SMALL_MIRV == weap_curr->type)
+		           || (CLUSTER_MIRV == weap_curr->type) )
+		         && ( y <= BOXED_TOP) ) {
+			startPoint = 0;
+			inh_yv     = std::abs(inh_yv);
+		}
+	}
+
+	// The spread can be created!
+	for (int32_t sc = 0; sc < subCount; ++sc) {
+		double   speed        = weapon[weap_curr->type].launchSpeed;
+		int32_t  newMissCount = sub_weap->countdown;
+		int32_t  newMissAngle = ROUND(
+					  (divStep * static_cast<double>(sc))
+					+  static_cast<double>(startPoint)
+					- (static_cast<double>(divergence) / 2.) );
+
+		// trace hard, but yield per sub mun
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+
+		// Manipulate angle if applicable
+		if (speedVar > 0.)
+			newMissAngle += ROUND(
+						  static_cast<double>(divergence)
+						* spreadVar
+						* Noise(randStart + 1054 + sc) );
+
+		// Be sure the angle is valid
+		while (newMissAngle < 0)
+			newMissAngle += 360;
+		newMissAngle %= 360;
+
+		// Manipulate number of submunition projectiles if applicable
+		if (sub_weap->countVariation > 0) {
+			newMissCount += ROUND(
+						  static_cast<double>(sub_weap->countdown)
+						* sub_weap->countVariation
+						* Noise(randStart + 78689 + sc) );
+			// This might go wrong, so be sure it doesn't
+			if (newMissCount <= 0)
+				newMissCount = 0;
+		}
+
+		// Manipulate launching speed if applicable
+		if (speedVar > 0)
+			speed += ROUND(speedVar * speed * Noise(randStart + 124786 + sc) );
+
+		// Launch new submunition missile
+		MISSILE mind_shot(player, sub_x, startY,
+		                  env.slope[newMissAngle][0] * speed * env.FPS_mod + inh_xv,
+		                  env.slope[newMissAngle][1] * speed * env.FPS_mod + inh_yv,
+		                  subType, MT_MIND_SHOT, ai_level);
+		mind_shot.update_submun(subPhys, newMissCount);
+
+		// Keep flying/rolling/digging/whatever until the missile hits something
+		// or the number of bounces is too high for this bot to keep track.
+		while (!mind_shot.destroy && (maxBounce >= mind_shot.bounced())) {
+			mind_shot.applyPhysics();
+
+			// Yield on each iteration to not hog the CPUs
+			if (!global.skippingComputerPlay)
+				std::this_thread::yield();
+		}
+
+		// If the missile is destroyed, the number of bounces is in order.
+		if (mind_shot.destroy) {
+			// The distance from the target must take both the direction
+			// of the last movement of the mind shot and the positions of
+			// both tanks into account:
+			int32_t tank_dir = SIGN(mem_curr->opX - x);
+			int32_t hit_dir  = SIGN(mem_curr->opX - mind_shot.x);
+			int32_t shot_dir = mind_shot.direction();
+
+			curr_overshoot = ABSDISTANCE2(mem_curr->opX + offset_x,
+			                              mem_curr->opY + offset_y,
+										  mind_shot.x,   mind_shot.y);
+
+			if (tank_dir == shot_dir)
+				curr_overshoot *= (tank_dir == hit_dir ? -1 :  1);
+			else
+				curr_overshoot *= (tank_dir == hit_dir ?  1 : -1);
+
+			// Note it down if this mind shot is better than the best
+			if (std::abs(curr_overshoot) < std::abs(cl_overshoot))
+				cl_overshoot = curr_overshoot;
+
+			// eventually add the score:
+			calcHitDamage(mind_shot.x, mind_shot.y, radius, sub_dmg,
+			              static_cast<weaponType>(subType));
+
+		}
+	} // End of looping submunitions
+
+	// Write back best overshoot if this is a MIRV or funky weapon.
+	// clusters and napalm weapons spread stuff out, there the hit
+	// of the parent weapon is what counts.
+	if ( (SMALL_MIRV   == weap_curr->type)
+	  || (FUNKY_BOMB   == weap_curr->type)
+	  || (FUNKY_DEATH  == weap_curr->type)
+	  || (CLUSTER_MIRV == weap_curr->type) )
+		curr_overshoot = cl_overshoot;
+	else
+		curr_overshoot = old_overshoot;
+}
+
+
+/** @brief Fire a mind shot and see where it goes.
+  *
+  * If the mind shot got destroyed, curr_overshoot is set to the distance of
+  * mem_curr->opponent to reached_x_/reached_y_. Positive values mean "too far",
+  * negative values mean the shot went "too short".
+  *
+  * @param[in]  trace_angle The angle to use, normally a spread variation of
+  *             curr_angle.
+  * @param[out] finished true if the mind shot got destroyed before reaching
+  *             maxBounce wall bounces/wraps.
+  * @param[out] top_wrapped Set to true if the the missile wrapped through a
+  *             wrap wall ceiling.
+  * @param[out] reached_x_ The x-coordinate on destruction. If the mind shot was
+  *             cancelled before being destroyed, @a reached_x_ is not changed.
+  * @param[out] reached_y_ The y-coordinate on destruction. If the mind shot was
+  *             cancelled before being destroyed, @a reached_y_ is not changed.
+  * @param[out] end_xv The x velocity the moment the projectile ends.
+  * @param[out] end_yv The y velocity the moment the projectile ends.
+  * @return true if all went well, false if any new() operator failed. If this
+  *         method returns false, the AI can no longer work.
+**/
+bool AICore::traceShot(int32_t trace_angle,
+                       bool &finished, bool &top_wrapped,
+                       int32_t &reached_x_, int32_t &reached_y_,
+                       double &end_xv, double &end_yv)
+{
+	double   top_x        = x;
+	double   top_y        = y;
+	double   vel_mod      = static_cast<double>(curr_power) * env.FPS_mod;
+	double   vel_x        = env.slope[trace_angle][0] * vel_mod / 100.;
+	double   vel_y        = env.slope[trace_angle][1] * vel_mod / 100.;
+	bool     can_top_wrap = ( env.isBoxed
+	                       && (WALL_WRAP == env.current_wallType)
+	                       && env.do_box_wrap );
+	double   old_yv       = 0;
+	double   old_y        = 0;
+
+	tank->getGuntop(trace_angle, top_x, top_y);
+
+	MISSILE mind_shot(player, top_x, top_y, vel_x, vel_y, weap_idx,
+	                  MT_MIND_SHOT, ai_level);
+
+	// Adapt missile drag if the player has dimpled/slick projectiles
+	if (player->ni[ITEM_DIMPLEP])
+		mind_shot.drag *= item[ITEM_DIMPLEP].vals[0];
+	else if (player->ni[ITEM_SLICKP])
+		mind_shot.drag *= item[ITEM_SLICKP].vals[0];
+
+	// Keep flying/rolling/digging/whatever until the missile hits something
+	// or the number of bounces is too high for this bot to keep track.
+	while (!mind_shot.destroy && (maxBounce >= mind_shot.bounced())) {
+		if (can_top_wrap) {
+			old_yv = vel_y;
+			old_y  = mind_shot.y;
+		}
+
+		mind_shot.applyPhysics();
+
+		if (can_top_wrap) {
+			mind_shot.getVelocity(vel_x, vel_y);
+			if ( ( (old_yv < 0.) && (vel_y < 0.) && (mind_shot.y > old_y) )
+			  || ( (old_yv > 0.) && (vel_y > 0.) && (mind_shot.y < old_y) ) )
+				top_wrapped = true;
+		}
+
+		if (!global.skippingComputerPlay)
+			std::this_thread::yield();
+	}
+
+	// If the missile is destroyed, the number of bounces is in order.
+	if (mind_shot.destroy) {
+		mind_shot.getVelocity(end_xv, end_yv);
+		finished      = true;
+		reached_x_    = mind_shot.x;
+		reached_y_    = mind_shot.y;
+
+		// The distance from the target must take both the direction
+		// of the last movement of the mind shot and the positions of
+		// both tanks into account:
+		int32_t tank_dir = SIGN(mem_curr->opX - x);
+		int32_t hit_dir  = SIGN(mem_curr->opX - reached_x_);
+		int32_t shot_dir = mind_shot.direction();
+
+		curr_overshoot = ABSDISTANCE2(mem_curr->opX + offset_x,
+		                              mem_curr->opY + offset_y,
+		                              reached_x_, reached_y_);
+
+		if (tank_dir == shot_dir)
+			curr_overshoot *= (tank_dir == hit_dir ? -1 :  1);
+		else
+			curr_overshoot *= (tank_dir == hit_dir ?  1 : -1);
+	} else {
+		finished       = false;
+		curr_overshoot = MAX_OVERSHOOT;
+	}
+
+	return true;
+}
+
+
+/** @brief trace all shots from weap_curr and fill in the arguments.
+  *
+  * Damage is recorded in the opponent memory dmgDone value.
+  *
+  * @return best overshoot.
+**/
+void AICore::traceWeapon(int32_t &has_crashed, int32_t &has_finished)
+{
+	assert(weap_curr && "ERROR: traceWeapon() with nullptr weap_curr?");
+
+	int32_t trace_overshoot= MAX_OVERSHOOT;
+	int32_t curr_reached_x = reached_x;
+	int32_t curr_reached_y = reached_y;
+	double  end_xv         = 0.;
+	double  end_yv         = 0.;
+
+	has_crashed    = 0;
+	has_finished   = 0;
+	curr_prime_hit = false;
+
+	// reset virtual damage on opponents.
+    opEntry_t* opp = mem_head;
+    while (opp) {
+		opp->dmgDone = 0;
+		opp = opp->next;
+    }
+
+	// Loop by spread, weapons that do not spread have a value of 1.
+	for (int32_t i = 0 ;
+		 canWork && !isStopped && (i < weap_curr->spread) ;
+		 ++i) {
+		int32_t tr_a     = curr_angle
+		                 + (  (SPREAD * i)
+		                    - (SPREAD * (weap_curr->spread - 1) / 2) );
+		bool    finished = false;
+		bool    top_wrap = false;
+		reached_x  = x;
+		reached_y  = y;
+
+		if (traceShot(tr_a, finished, top_wrap, curr_reached_x, curr_reached_y,
+		              end_xv, end_yv)
+		  && finished) {
+
+			++has_finished;
+
+			// Check whether the shot crashed
+			if ( ( env.isBoxed
+			    && ( (curr_reached_y <= BOXED_TOP ) // crashed on top
+			    // wrapped to bottom with dirt above is a ceiling crash, too.
+			      || ( top_wrap
+			        && ( (weap_curr->type < BURROWER) || (weap_curr->type > PENETRATOR) )
+				    && (curr_reached_y > global.surface[curr_reached_x].load()) ) ) )
+			  // Steel wall have additional wall crashes
+			  || ( (WALL_STEEL == env.current_wallType)
+			    && (weap_curr->subMunCount < 1) // Clusters never crash
+				&& ( (curr_reached_x <= 2)
+				  || (curr_reached_x >= (env.screenWidth - 3) ) ) ) ) {
+
+				++has_crashed;
+			}
+
+			if (weap_curr->subMunCount > 0) {
+				double inh_xv = weapon[weap_curr->type].impartVelocity * end_xv;
+				double inh_yv = weapon[weap_curr->type].impartVelocity * end_yv;
+				// Trace the cluster parts and add their score:
+				traceCluster(weap_curr->subMunType, weap_curr->subMunCount,
+				             curr_reached_x, curr_reached_y, inh_xv, inh_yv);
+			} else
+				// Calculate a score for the hit, crashes are handled later
+				calcHitDamage(curr_reached_x, curr_reached_y,
+				              static_cast<double>(weap_curr->radius),
+				              weap_curr->dmgSingle,
+				              static_cast<weaponType>(weap_curr->type));
+
+			// Note best overshoot if any
+			if (std::abs(curr_overshoot) < std::abs(trace_overshoot)) {
+				trace_overshoot = curr_overshoot;
+				reached_x       = curr_reached_x;
+				reached_y       = curr_reached_y;
+			}
+		} // End of if traceShot
+	} // end of spread loop
+
+	// Write back best data found:
+	curr_overshoot = trace_overshoot;
+}
+
+
+/// @brief Set a new score to an items entry
+void AICore::updateItemScore(itEntry_t* pItem)
+{
+	/* There aren't many items that are actually usable.
+	 * 1. Teleporters
+	 *    These can be used to get out of a buried scenario.
+	 *    Further they might be an alternative if the
+	 *    targeted tank is far away.
+	 * 2. Fan
+	 *    This item has no real use for the AI but one:
+	 *    If the enemy is behind a mountain and the AI has tail wind,
+	 *    then it might be helpful to change the wind direction.
+	 *    However, this does only make sense if not that many other
+	 *    bots have their shot until this one gets its next try.
+	 * 3. Self destruct devices
+	 *    If this bots tanks is almost dead, and the preferred target
+	 *    has a lot of health left, then trying to take them with us
+	 *    using a big boom is somewhat compelling.
+	 * 4. Fuel and rockets.
+	 *    Fuel can be used to get away from a steep wall, rockets can
+	 *    be used to get out of a steep canyon.
+	 */
+
+
+	// === Get out quickly if the chosen item is out of stock ===
+	// ==========================================================
+	if (0 == pItem->amount) {
+		pItem->score = -50000;
+		return;
+	}
+
+
+	// === Only evaluate items that are available ===
+	// ==============================================
+	if (!env.isItemAvailable(pItem->type + WEAPONS)) {
+		pItem->score = -100000;
+		return;
+	}
+
+	DEBUG_LOG_AI(player->getName(), "Evaluating score for %s",
+	             item[pItem->type].getName())
+
+	// reset helper boolean
+	pItem->escape = false;
+
+
+	/* -------------------------------------------------------------
+	 * --- 1) Set score for freeing capabilities while buried    ---
+	 * ------------------------------------------------------------- */
+	double unbury_score = 0;
+    if (buried > BURIED_LEVEL) {
+		double bury_diff = buried - BURIED_LEVEL;
+		double off_mod   = (player->defensive - 1.5) / -2.;
+
+		if (ITEM_TELEPORT == pItem->type)
+			unbury_score = bury_diff * ai_level * off_mod
+			               // It is more valuable when the target is buried,
+			               // as it is not desirable to swap with them
+			             * (mem_curr->is_buried ? 3. : 1.);
+
+		else if (ITEM_SWAPPER == pItem->type)
+			unbury_score = bury_diff * ai_level * off_mod * 2.
+			               // If the target is buried, the swapper is no good.
+			             * (mem_curr->is_buried ? -50. : 2.);
+
+		else if (ITEM_MASS_TELEPORT == pItem->type)
+			unbury_score = bury_diff * ai_level * off_mod * 1.25;
+
+		else if (ITEM_FAN == pItem->type)
+			// at least the bot might think the usage is safe.
+			unbury_score = bury_diff
+			             * static_cast<double>(maxAiLevel + 1 - ai_level)
+			             * off_mod / 2.;
+		else
+			// Everything else is useless
+			unbury_score = -5000.;
+
+		// "escape" tool ?
+		if ( (ITEM_TELEPORT <= pItem->type)
+		  && (ITEM_MASS_TELEPORT >= pItem->type) )
+			pItem->escape = true;
+    }
+
+
+	/* -------------------------------------------------------------
+	 * --- 2) The wind direction change score for fans           ---
+	 * ------------------------------------------------------------- */
+	double fan_score = 0.;
+	if ( (ITEM_FAN == pItem->type)
+	  // The fan can only be considered useful if the bot has tail wind:
+	  && ( ( (mem_curr->opX > tank->x) && (global.wind > 0.) )
+	    || ( (mem_curr->opX < tank->x) && (global.wind < 0.) ) ) ) {
+
+        // First count how many other bots can have their turn until
+        // this one will get its next chance:
+        opEntry_t* check   = mem_head;
+        int32_t    between = 0;
+        while (check) {
+			if ( (check->entry->opponent != player) && check->alive)
+				++between;
+			check = check->next;
+        }
+
+		// Now look whether there is a mountain in between:
+		int32_t check_x   = ROUND(mem_curr->opX);
+		int32_t checked   = 0;
+		int32_t direction = SIGN(global.wind) * -1;
+		int32_t range_x   = 10 * (ai_level + RAND_AI_0P);
+		int32_t top_ledge = env.screenHeight;
+
+		while ( (checked < range_x)
+		     && (check_x > 1)
+		     && (check_x < (env.screenWidth - 2)) ) {
+			int32_t check_y = global.surface[check_x].load(ATOMIC_READ);
+			if (check_y < top_ledge)
+				top_ledge = check_y;
+			check_x += direction;
+		}
+
+		// Now the score is a simple height difference modified by defensiveness
+		// (The more defensive the player is, the more it is inclined to prepare
+		//  the next attack instead of pissing them of with a weak shot.)
+		fan_score = static_cast<double>(top_ledge - mem_curr->opY)
+		          * (player->defensive + ai_level_d);
+
+		// However, the score is multiplied again with the count of opponents
+		// that will have their try until this one gets its next shot.
+		fan_score *= fan_score > 0.
+		           ? static_cast<double>(ai_level - between) // normal multiplier
+		           : static_cast<double>(between); // The more bots, the more useless.
+
+		// However, if the bot already used the fan in the last round,
+		// do not repeat, no matter what:
+		if (last_weap == (ITEM_FAN + WEAPONS)) {
+			DEBUG_LOG_EMO(player->getName(),
+			              "=> Reducing fan score %6.2lf to %62lf (no repeat)",
+			              fan_score, std::abs(fan_score * ai_level) * -1.);
+			fan_score = std::abs(fan_score * ai_level) * -1.;
+		}
+	} // End of calculating a fan score
+
+
+	/* -------------------------------------------------------------
+	 * --- 3) Set score for self destruct probability            ---
+	 * ------------------------------------------------------------- */
+    double selfde_score = 0.;
+    if ( (mem_curr->opLife > (currLife * 10.))
+	  || (isShocked && (mem_curr->opLife > (currLife *  5.))) ) {
+		if ( (ITEM_VENGEANCE >= pItem->type)
+		  && (ITEM_FATAL_FURY <= pItem->type) ) {
+			selfde_score = static_cast<double>(pItem->type - ITEM_VENGEANCE + 1)
+			             * mem_curr->diffLife / (player->selfPreservation + .5);
+		}
+    }
+
+
+	/* -------------------------------------------------------------
+	 * --- 4) The "useless" score, for not usable items          ---
+	 * ------------------------------------------------------------- */
+	double useless_score = 0.;
+	if ( (ITEM_FATAL_FURY < pItem->type)
+	  && (ITEM_ROCKET != pItem->type) )
+		useless_score = -50000.;
+	/// @todo : FUEL should be made available to the AI somehow.
+
+
+	/* -------------------------------------------------------------
+	 * --- 5) Sum up the score                                   ---
+	 * --- This will be used for sorting the items list          ---
+	 * ------------------------------------------------------------- */
+	double pref_score = pItem->preference / static_cast<double>(ai_level * 10);
+
+	double xScore = unbury_score + selfde_score + useless_score + fan_score;
+
+	if (useless_score > -1.) {
+		DEBUG_LOG_EMO(player->getName(), "  preference   : %6.2lf%s", pref_score,
+					xScore > 1. ? "" : " (ignored)")
+		DEBUG_LOG_EMO(player->getName(), "  unbury_score : %6.2lf", unbury_score)
+		DEBUG_LOG_EMO(player->getName(), "  fan_score    : %6.2lf", fan_score)
+		DEBUG_LOG_EMO(player->getName(), "  selfde_score : %6.2lf", selfde_score)
+		DEBUG_LOG_EMO(player->getName(), "  useless_score: %6.2lf", useless_score)
+	}
+
+	// Only add preferences if there is any use for the item:
+	if (xScore > 1.)
+		xScore += pref_score;
+
+	pItem->score = ROUND(xScore);
+
+	DEBUG_LOG_EMO(player->getName(), "  Final Score  : %8d", pItem->score)
+}
+
+
+/// @brief Set a new score to an opponents entry
+void AICore::updateOppScore(opEntry_t* pOpp)
+{
+    sOpponent* entry    = pOpp->entry;
+	PLAYER*    opponent = entry->opponent;
+	TANK*      oppTank  = opponent->tank;
+
+	DEBUG_LOG_AI(player->getName(), "Evaluating score for %s", opponent->getName())
+
+	/* Quickly handle dead tanks and the own entry */
+	if ( (player == opponent) || !oppTank || oppTank->destroy ) {
+		entry->damage_from += entry->damage_last;
+		entry->damage_last  = 0;
+		pOpp->score = (player == opponent) ? -2 : -1;
+
+		DEBUG_LOG_AI(player->getName(), "%s%s",
+		             player == opponent ? "" : opponent->getName(),
+		             player == opponent
+		             ? "Not evaluating myself!"
+		             : " is dead and not selectable!")
+		return;
+	}
+
+
+	/* -------------------------------------------------------------
+	 * --- 1) Set up a fear value (if needed)                    ---
+	 * --- This is used to possibly trigger actions that may not ---
+	 * --- be wise but are imposed by a sudden surge of fear.    ---
+	 * ------------------------------------------------------------- */
+	double fear_damage = 0.;
+	double fear_shock  = 0.;
+	if (!pOpp->onSameTeam) {
+		entry->fear_shock = 0.;
+
+		// The higher the AI level, the more the taken over fear is
+		// reduced. *But* the more defensive the player is, the less
+		// it is reduced. (Even more on a lucky turn. ;-)
+		// Ranges are from useless full defensive to deadly full offensive:
+		// From: 2.5 + 1 - ( 1 + 1) => 3.5 - 2 => 1.5 (only one third reduced)
+		// To  : 2.5 + 5 - (-1 + 1) => 7.5 - 0 => 7.5 (~87% taken off)
+		entry->fear /= 2.5 + ai_level_d
+		             - (player->defensive + 1.0);
+
+		// Only add new fear if there was any damage
+		if (entry->damage_last > 0) {
+			fear_damage  = player->painSensitivity * entry->damage_last;
+			entry->fear += player->selfPreservation;
+		}
+
+		// first fear check:
+		// If the AI can not stand the pain, the damage done is multiplied
+		// with the fear value. This does not trigger any action, yet, but
+		// the score will go up a lot.
+		fear_shock = entry->fear - static_cast<double>(RAND_AI_0P);
+		if ( (fear_damage > 0.) && (fear_shock > 0.) ) {
+			fear_damage *= entry->fear;
+			DEBUG_LOG_EMO(player->getName(),
+			              "%s caused fear shock %lf with damage %u",
+			              opponent->getName(), fear_shock,
+			              ROUNDu(fear_damage))
+			// Is this the new shocker?
+			if ( (nullptr == shocker)
+			  || (fear_shock > shocker->fear_shock) ) {
+				DEBUG_LOG_EMO(player->getName(),
+				              "%s %s%s%s as new shocker", opponent->getName(),
+				              shocker ? "replaces" : "set",
+				              shocker ? " " : "",
+				              shocker ? shocker->opponent->getName() : "")
+				shocker = entry;
+			}
+		} // End of having a fear shock
+		entry->fear_shock = fear_shock;
+	} // end of fear value handling
+
+	/* -------------------------------------------------------------
+	 * --- 2) Check damage for whether revenge is called for     ---
+	 * ------------------------------------------------------------- */
+	double revenge_score = 0.;
+	if ( (entry->damage_last > 0) && !pOpp->onSameTeam) {
+
+		// First reduce the current damage accumulated. More for lower level bots.
+		if (!pOpp->revengeDone) {
+			DEBUG_LOG_EMO(player->getName(), "Current anger damage from %s: %d",
+						 opponent->getName(), entry->revenge_dmg)
+
+			entry->revenge_dmg /= 4.5 - ai_type_mod;
+
+			DEBUG_LOG_EMO(player->getName(), " --> Anger cooled down to   : %d",
+						 entry->revenge_dmg)
+
+			// Add current damage
+			entry->revenge_dmg += entry->damage_last;
+
+			DEBUG_LOG_EMO(player->getName(), " --> Anger raised again to  : %d",
+						 entry->revenge_dmg)
+
+			// Revenge damage handled:
+			pOpp->revengeDone = true;
+		}
+
+		// Now see whether a new act of vengeance is initiated:
+		if ( (entry->revenge_dmg > (player->vengeanceThreshold * maxLife))
+		  && ( (rand() % 100) <= player->vengeful) ) {
+
+			// Okay, the potential is there...
+			revenge_score = static_cast<double>(entry->damage_last * player->vengeful)
+			              / 100.;
+			if ( (nullptr == revengee)
+			  || (entry->revenge_dmg > revengee->revenge_dmg) ) {
+				// A new one!
+				DEBUG_LOG_EMO(player->getName(), " --> [%d] %s %s%s for revenge!",
+				             entry->revenge_dmg,
+				             entry->opponent->getName(),
+				             revengee ? "replaces " : "is set ",
+				             revengee ? revengee->opponent->getName() : "")
+				revengee = entry;
+			}
+		}
+	} // end of revenge value handling
+
+
+	/* -------------------------------------------------------------
+	 * --- 3) Check opponents health compared to this tank       ---
+	 * --- The more health they got, the more money can be made. ---
+	 * --- On the other hand, the bigger the difference, the     ---
+	 * --- more impressive they are.                             ---
+	 * -------------------------------------------------------------
+	 */
+	double life_score = 0.;
+	if (pOpp->diffLife < 0.) {
+		// The opponent has more health. This might impress the bot:
+		if ( (rand() % static_cast<int32_t>(DEADLY_PLAYER)) < ai_level) {
+			// No, there is nothing impressive with that...
+			life_score = (player->defensive - 3.) / 2. * pOpp->diffLife;
+			// Note:
+			// Full Defensive : (-1 - 3) / 2 * -x => -4 / 2 * -x => -2 * -x = 2 * x
+			// Full Offensive : ( 1 - 3) / 2 * -x => -2 / 2 * -x => -1 * -x = 1 * x
+
+			// If the bot needs money, the opponents health might be added:
+			if (needMoney && RAND_AI_0P)
+				life_score += pOpp->opLife;
+		}
+	} else
+		// Add points for their weakness, more if the bot is offensive
+		life_score = (player->defensive + 3.) / 2. * pOpp->diffLife;
+		// Note:
+		// Full Defensive : (-1 + 3) / 2 * x => 2 / 2 * x = 1 * x
+		// Full Offensive : ( 1 + 3) / 2 * x => 4 / 2 * x = 2 * x
+
+
+	/* -------------------------------------------------------------
+	 * --- 4) Add points for distance                            ---
+	 * --- The theory is, that weaker bots concentrate on nearer ---
+	 * --- enemies first, while stronger bots do not mind.       ---
+	 * ------------------------------------------------------------- */
+	double dist_score = (static_cast<double>(env.halfWidth) - pOpp->distance)
+	                  / ai_level_d;
+
+
+	/* -------------------------------------------------------------
+	 * --- 5) Add points the easier the target is to be killed.  ---
+	 * --- The easier, and cheaper, the better. But even much    ---
+	 * --- better if this bot needs money.                       ---
+	 * ------------------------------------------------------------- */
+	double vict_score = 0.;
+	double vict_mod   = needMoney ? static_cast<double>(8 - ai_level) : 1.;
+	if (pOpp->opLife < blast_max)
+		vict_score += vict_mod * (blast_max - pOpp->opLife) * 1. * ai_type_mod;
+	if (pOpp->opLife < blast_big)
+		vict_score += vict_mod * (blast_big - pOpp->opLife) * 2. * ai_type_mod;
+	if (pOpp->opLife < blast_med)
+		vict_score += vict_mod * (blast_med - pOpp->opLife) * 4. * ai_type_mod;
+	if (pOpp->opLife < blast_min)
+		vict_score += vict_mod * (blast_min - pOpp->opLife) * 8. * ai_type_mod;
+
+
+	/* --------------------------------------------------------------
+	 * --- 6) Add or dock points regarding AI level               ---
+	 * --- More powerful opponents are targeted preferably, while ---
+	 * --- weaker ones are not considered to be such a threat.    ---
+	 * --- Note: Human players are handled like deadly bots.      ---
+	 * -------------------------------------------------------------- */
+	double level_score = 0.;
+	if (!pOpp->onSameTeam) {
+        if ( (HUMAN_PLAYER     < opponent->type)
+		  && (LAST_PLAYER_TYPE > opponent->type) )
+			level_score = static_cast<double>(opponent->type - player->type);
+		else
+			level_score = static_cast<double>(DEADLY_PLAYER  - player->type);
+
+		// The higher the self preservation, the more urgent deadlier
+		// bots are targeted to get them down early.
+		if (level_score > 0.)
+			level_score *= player->selfPreservation + 1.;
+
+		// The more defensive the bot is, the more does it want to target
+		// weaker opponents to not aggravate the stronger ones
+		if (level_score < 0.)
+			level_score *= player->defensive + 2.;
+
+		// The more health this bots tank has, the more prominent is this score
+		level_score = std::abs(level_score) * currLife;
+	}
+
+
+	/* -------------------------------------------------------------
+	 * --- 7) Add points for score difference                    ---
+	 * --- Target the leading bots earlier, losing ones later.   ---
+	 * ------------------------------------------------------------- */
+	double win_score = pOpp->onSameTeam ? 0. :
+	                   (opponent->score - player->score)
+	                   * (player->selfPreservation + 1.)
+	                   * (player->defensive + 2.)
+	                   * static_cast<double>(ai_level + 1)
+	                   * (static_cast<double>(pOpp->opLife) / 10.)
+	                   / (player->painSensitivity + 0.5);
+	// Note: The win_score is only used if positive.
+	// 1 - Self preservation: Get rid of the winner as a threat soon.
+	// 2 - Defensiveness : Even more if of the defensive type.
+	// 3 - The smarter the more they do care.
+	// 4 - Multiply with 10% of the opponents tank life
+	// 5 - Pain Sensitivity: Can they stand the answer? ( If this value
+	//     is lower than 0.5, they care so less, that the score is raised. Up
+	//     to a doubling is possible - If they really feel no pain.)
+	// Maximum score:
+	// deadly + 1 (lucky turn), maximum defensiveness and self preservation, painless:
+	// (4 * 3 * 7) / 0.5 = 84 / 0.5 = 168 points per opponent health point and
+	// round win difference.
+
+
+	/* -------------------------------------------------------------
+	 * --- 8) Sum up the score                                   ---
+	 * --- This will be used for sorting the opponents list      ---
+	 * ------------------------------------------------------------- */
+	double damage_score = entry->damage_from - entry->damage_to;
+	double kill_score   = (entry->killed_me   - entry->killed_them) * maxLife;
+	double last_score   = entry->damage_last * type_mod;
+
+	DEBUG_LOG_EMO(player->getName(), "  team_mod     : %6.2lf", pOpp->team_mod)
+	DEBUG_LOG_EMO(player->getName(), "  type_mod     : %6.2lf", type_mod)
+	DEBUG_LOG_EMO(player->getName(), "  damage_score : %6.2lf", damage_score)
+	DEBUG_LOG_EMO(player->getName(), "  kill_score   : %6.2lf", kill_score)
+	DEBUG_LOG_EMO(player->getName(), "  last_score   : %6.2lf", last_score)
+	DEBUG_LOG_EMO(player->getName(), "  fear_damage  : %6.2lf", fear_damage)
+	DEBUG_LOG_EMO(player->getName(), "  revenge_score: %6.2lf", revenge_score)
+	DEBUG_LOG_EMO(player->getName(), "  life_score   : %6.2lf", life_score)
+	DEBUG_LOG_EMO(player->getName(), "  dist_score   : %6.2lf", dist_score)
+	DEBUG_LOG_EMO(player->getName(), "  vict_score   : %6.2lf", vict_score)
+	DEBUG_LOG_EMO(player->getName(), "  level_score  : %6.2lf", level_score)
+	DEBUG_LOG_EMO(player->getName(), "  win_score    : %6.2lf", win_score)
+
+	double xScore = ( damage_score  > 0. ? pOpp->team_mod * damage_score  : 0. )
+	              + ( kill_score    > 0. ? pOpp->team_mod * kill_score    : 0. )
+	              + ( last_score    > 0. ? pOpp->team_mod * last_score    : 0. )
+	              + ( fear_damage   > 0. ? pOpp->team_mod * fear_damage   : 0. )
+	              + ( fear_shock    > 0. ?    fear_shock * fear_damage   : 0. )
+	              + ( revenge_score > 0. ? pOpp->team_mod * revenge_score : 0. )
+	              + ( life_score    > 0. ? pOpp->team_mod * life_score    : 0. )
+	              + ( vict_score    > 0. ? pOpp->team_mod * vict_score    : 0. )
+	              + ( win_score     > 0. ? win_score                     : 0. )
+	              + dist_score + level_score;
+	pOpp->score = ROUND(xScore);
+
+	DEBUG_LOG_EMO(player->getName(), "  Final Score  : %8d", pOpp->score)
+
+	// --- clean up damage_last ---
+	if (entry->damage_last) {
+		entry->damage_from += entry->damage_last;
+		entry->damage_last  = 0;
+	}
+}
+
+
+/// @brief Set a new score to a weapons entry
+void AICore::updateWeapScore(weEntry_t* pWeap)
+{
+	// As this is used a few dozen times, a shortcut to pWeap->type is nice:
+	weaponType wType = pWeap ? static_cast<weaponType>(pWeap->type) : SML_MIS;
+
+
+	// === Get out quickly if the chosen item is out of stock ===
+	// ==========================================================
+	if (0 == pWeap->amount) {
+		pWeap->score = -50000;
+		return;
+	}
+
+
+	// === Only evaluate items that are available ===
+	// ==============================================
+	if (!env.isItemAvailable(wType)) {
+		pWeap->score = -100000;
+		return;
+	}
+
+	DEBUG_LOG_AI(player->getName(), "Evaluating score for %s",
+	             weapon[wType].getName())
+
+	// reset boolean helpers
+	pWeap->blastOut = false;
+	pWeap->kamikaze = false;
+
+
+	// === If no opponent is chosen (however this may happen) then ===
+	// === the pure preferences count.                             ===
+	// ===============================================================
+	if (nullptr == mem_curr) {
+		pWeap->score = pWeap->preference;
+		DEBUG_LOG_AI(player->getName(), " -> Use preference %d", pWeap->preference)
+		return;
+	}
+
+
+	// === If this is a laser, it will only be evaluated if the tank is ===
+	// === not below this players tanks as it can not be reached then.  ===
+	// ====================================================================
+	if ( (SML_LAZER <= wType) && (LRG_LAZER >= wType)
+	  && (mem_curr->opY > y) ) {
+		pWeap->score = -45000;
+		DEBUG_LOG_AI(player->getName(), " -> Target y %d is not reachable from %d",
+		             ROUND(mem_curr->opY), ROUND(y))
+		return;
+	}
+
+
+	// === If this is the percent bomb, its damage must be adapted, ===
+	// === as it depends on the selected target.                    ===
+	// ================================================================
+	if (PERCENT_BOMB == wType) {
+		pWeap->dmgCluster = 0.;
+		pWeap->dmgSingle  = mem_curr->opLife / 2;
+		pWeap->dmgSpread  = pWeap->dmgSingle;
+	}
+
+	// === The same applies to the reducer ===
+	if (REDUCER == wType) {
+		pWeap->dmgCluster = 0.;
+		pWeap->dmgSingle  = (mem_curr->opLife / 2.)
+		                  * (mem_curr->entry->opponent->damageMultiplier / 2.)
+		                  * (player->painSensitivity + ai_over_mod)
+		                  / (-1. * (player->defensive - 1.25 - ai_over_mod));
+		pWeap->dmgSpread  = pWeap->dmgSingle;
+	}
+
+
+	/* -------------------------------------------------------------
+	 * --- 1) Set score for reaching the target health           ---
+	 * ------------------------------------------------------------- */
+	double weap_dmg    = 0.; // Filled here, used for splash score, too
+	double dmg_diff    = 0.;
+	double point_score = mem_curr->opLife;
+	// If the bot is shocked, spread and cluster weapons get a bonus:
+	double shock_bonus = isShocked
+	                   ? static_cast<double>(maxAiLevel - ai_level) + ai_over_mod
+	                   : 1.;
+
+	if (pWeap->dmgCluster > 1.) {
+		weap_dmg = pWeap->dmgCluster;
+		dmg_diff = (weap_dmg / ai_over_mod) - point_score;
+		if (dmg_diff > 0.)
+			dmg_diff *= shock_bonus;
+	} else if (pWeap->dmgSpread > pWeap->dmgSingle) {
+		weap_dmg = pWeap->dmgSpread;
+		dmg_diff = (weap_dmg / (ai_over_mod / 2.)) - point_score;
+		if (dmg_diff > 0.)
+			dmg_diff *= shock_bonus;
+	} else if (pWeap->dmgSingle > 1.) {
+		weap_dmg = pWeap->dmgSingle;
+		dmg_diff = weap_dmg / ai_over_mod - point_score;
+	} else
+		dmg_diff = -point_score;
+
+	if (dmg_diff < 0.)
+		// Too less damage
+		point_score += dmg_diff;
+	else if (dmg_diff > 0.)
+		// Otherwise chop off a modified difference
+		point_score -= dmg_diff
+		             / ( -(player->defensive - 2.5) // 3.5 full offensive, 2.5 full defensive
+		               * ai_over_mod ); // the higher the level, the more the reduction.
+
+	// If this is a REDUCER or dirt weapon, and the fake damage is
+	// higher than the target health, modify the score. The AI wants
+	// to finish off the almost dead and not debuff them
+	if ( (REDUCER == wType)
+	  || ( (DIRT_BALL <= wType) && (SMALL_DIRT_SPREAD >= wType) ) ) {
+		if (mem_curr->opLife <= blast_min)
+			point_score = 0;
+		else if (mem_curr->opLife <= blast_med)
+			point_score /= static_cast<double>(ai_level + 3);
+		else if (mem_curr->opLife <= blast_big)
+			point_score /= static_cast<double>(ai_level + 1) / ai_over_mod;
+		else if (dmg_diff > 0.)
+			point_score /= ai_over_mod;
+	}
+
+
+	/* -------------------------------------------------------------
+	 * --- 2) check buried state, shaped charges and the driller ---
+	 * ---    might still be usable.                             ---
+	 * ------------------------------------------------------------- */
+	double unbury_score = 0.;
+	if (buried > BURIED_LEVEL) {
+		// Shaped charges refer to the y coordinate
+		if ( (SHAPED_CHARGE <= wType)
+		  && (CUTTER        >= wType)
+		  && (std::abs(mem_curr->opY - y) < (weapon[wType].radius / 20))
+		  && (mem_curr->distance < weapon[wType].radius) )
+			// This one is usable.
+			unbury_score = pWeap->dmgSingle * ai_over_mod;
+
+		// The driller is only usable in a vertical way:
+		else if ( (DRILLER == wType)
+		  && (std::abs(mem_curr->opX - x) < (weapon[wType].radius / 20))
+		  && (mem_curr->distance < weapon[wType].radius) )
+			unbury_score = pWeap->dmgSingle * ai_over_mod;
+
+		// Riot bombs and charges are the ultimate tools, of course
+		else if ( ( (RIOT_CHARGE <= wType) && (RIOT_BLAST    >= wType) )
+				||( (RIOT_BOMB   <= wType) && (HVY_RIOT_BOMB >= wType) ) )
+			unbury_score = ai_type_mod
+			             * static_cast<double>(weapon[wType].radius)
+			             * static_cast<double>(buried - BURIED_LEVEL + ai_level);
+
+		// Everything else is (mostly) useless
+		else {
+			if (pWeap->dmgCluster > 1.)
+				unbury_score -= ai_type_mod * pWeap->dmgCluster * pWeap->dmgSingle;
+			else {
+				unbury_score -= ai_type_mod * (pWeap->dmgSpread + pWeap->dmgSingle);
+				// However, if the target is in range and a self hit would not
+				// kill our own tank...
+				if ( (mem_curr->distance < weapon[wType].radius)
+				  && (currLife > pWeap->dmgSingle)
+				  && (currLife > pWeap->dmgSpread) )
+					unbury_score += ai_over_mod * pWeap->dmgSingle / player->selfPreservation;
+			}
+		} // End of "useless" weapons
+
+		if (unbury_score > 1.)
+			pWeap->blastOut = true;
+	} // End of unbury score.
+
+	// If not buried, riot weapons are useless:
+	else if ( ( (RIOT_CHARGE <= wType) && (RIOT_BLAST    >= wType) )
+	        ||( (RIOT_BOMB   <= wType) && (HVY_RIOT_BOMB >= wType) ) )
+		unbury_score = -50000.;
+
+
+	/* -------------------------------------------------------------
+	 * --- 3) Panic score - If this bot has panicked, the more   ---
+	 * ---    damage the better.                                 ---
+	 * ------------------------------------------------------------- */
+	double panic_score = 0.;
+	if (isShocked && (mem_curr->entry == shocker) && (buried <= BURIED_LEVEL) ) {
+		panic_score = pWeap->dmgCluster > 1.
+		            ? pWeap->dmgCluster
+		            : pWeap->dmgSpread;
+		// If this is a debuffing weapon like reducer or percent bomb,
+		// it is valued even higher.
+		if (REDUCER == wType)
+			panic_score += mem_curr->entry->opponent->damageMultiplier
+			             * mem_curr->opLife * player->selfPreservation;
+		else if (PERCENT_BOMB == wType)
+			panic_score += pWeap->dmgSingle / player->selfPreservation;
+		else if ((DIRT_BALL         <= wType)
+			  && (SMALL_DIRT_SPREAD >= wType))
+			panic_score += weapon[wType].radius
+			             * weapon[wType].spread
+			             * (1.5 + player->defensive);
+	}
+
+
+	/* ----------------------------------------------------------------------
+	 * --- 4) Score for reaching buried opponents.                        ---
+	 * ---    If an opponent is buried, burrowers and tremors are useful. ---
+	 * ---------------------------------------------------------------------- */
+	double dig_score = 0.;
+	if ( mem_curr->is_buried
+	  || ( (mem_curr->opX > x) && (mem_curr->buried_l >= BURIED_LEVEL_HALF) )
+	  || ( (mem_curr->opX < x) && (mem_curr->buried_r >= BURIED_LEVEL_HALF) ) ) {
+
+		// Chain weapons can push through dirt, but are bad when the own tank
+		// is buried.
+		if ( (CHAIN_GUN <= wType) && (JACK_HAMMER >= wType) )
+			dig_score = pWeap->dmgSingle
+			          * static_cast<double>(weapon[wType].getDelayDiv())
+			          / (1.75 + player->defensive)
+			          * (buried > BURIED_LEVEL ? -1. : 1.);
+
+		// Burrowers can actually directly reach the target
+		else if ( (BURROWER <= wType) && (PENETRATOR >= wType) )
+			dig_score = pWeap->dmgSingle * ai_type_mod;
+
+		// tremors are somewhat weak, but the do not only (possibly) reach
+		// the target but remove dirt as well.
+		else if ( (TREMOR <= wType) && (TECTONIC >= wType) )
+			dig_score = (pWeap->dmgSingle + weapon[wType].radius)
+			          * ai_over_mod * (2.1 + player->defensive);
+
+		// Riot bombs are useful to undig an opponent as well.
+		else if ( (RIOT_BOMB <= wType) && (HVY_RIOT_BOMB >= wType) )
+			dig_score = weapon[wType].radius
+			            // Note: pain sensitivity is used, as not doing any
+			            //       damage won't trigger a vengeance reaction.
+			          * (1. + player->defensive + player->painSensitivity);
+
+		// remember that this is chosen for blasting out an opponent:
+		if (dig_score > 1.)
+			pWeap->blastOut = true;
+	}
+
+
+	/* --------------------------------------------------------------
+	 * --- 5) Splash damage                                       ---
+	 * --- Check all tanks whether they are in "splash range" and ---
+	 * --- add or dock points according to the team_mod value of  ---
+	 * --- the hit tanks. This score can be negative and is meant ---
+	 * --- to help bots to decide against oversized weapons if    ---
+	 * --- good working alternatives are present.                 ---
+	 * -------------------------------------------------------------- */
+	double splash_score = 0.;
+	double money_made   = 0.; // build here, used below
+	double money_cost   = 0.; // build here, used below
+	if (buried <= BURIED_LEVEL) {
+		opEntry_t* op    = mem_head;
+
+		// Always assume a full direct hit:
+		double xhit = mem_curr->opX;
+		double yhit = mem_curr->opY;
+
+		// The minimum in_rate depends on the defensive level. EXPLOSION takes
+		// different values for the shaped weapons and tectonics. Further the
+		// full rate limit is 10% damage. The bot does not calculate minimum
+		// axis rates, and the full rate limit might become lower or higher than
+		// this 10%. This is wanted as bots "only estimate".
+		double rate_limit = (player->defensive + .75) / 10.;
+		// result: Over-offensive Sith: (-1.25 + 0.75) / 10. => 0.5 / 10. =>  5%
+		//         Over-defensive Jedi: ( 1.25 + 0.75) / 10. => 2.0 / 10. => 20%
+
+		while (op) {
+
+			// Do not evaluate the target, as it will get the hit anyway
+			if (op == mem_curr) {
+				op =op->next;
+				continue;
+			}
+
+			// Yield on each iteration to not hog the CPUs
+			if (!global.skippingComputerPlay)
+				std::this_thread::yield();
+
+			PLAYER* pl = op->entry->opponent;
+			TANK*   lt = pl ? pl->tank : nullptr; // short cut
+
+			if (!lt || lt->destroy || (op->opLife < 1.)) {
+				// irrelevant
+				op = op->next;
+				continue;
+			}
+
+			double weap_rad  = weap_curr->radius;
+			double xrad      =    DRILLER       == wType
+			                 ? weap_rad / 20. : weap_rad;
+			double yrad      = ( (SHAPED_CHARGE <= wType)
+							  && (CUTTER        >= wType) )
+			                 ? weap_rad / 20. : weap_rad;
+			double in_rate_x = 0.;
+			double in_rate_y = 0.;
+
+			if (lt->isInEllipse(xhit, yhit, xrad, yrad, in_rate_x, in_rate_y)) {
+				double in_rate = in_rate_x * in_rate_y;
+
+				if (in_rate < rate_limit)
+					in_rate = rate_limit;
+
+				double score = std::min(weap_dmg * in_rate, op->opLife)
+				             * op->team_mod;
+
+				// Do not overdo positive scores
+				if (score >= 0)
+					// Note: That is [1.1;4.8]
+					score /= ai_over_mod * static_cast<double>((ai_level + 1) / 2);
+
+				splash_score += score;
+
+				DEBUG_LOG_EMO(player->getName(),
+				              "%s in splash range, %s %d points",
+				              pl == player ? "I am" : pl->getName(),
+				              score > 0 ? "add " : "dock",
+				              std::abs(ROUND(score)))
+
+				// Note down money made or cost:
+				if (op->team_mod > 0.)
+					money_made += ( std::min(weap_dmg * in_rate, op->opLife)
+					              * static_cast<double>(env.scoreHitUnit) )
+					            + ( (weap_dmg * in_rate) >= op->opLife
+					                ? static_cast<double>(env.scoreUnitDestroyBonus)
+					                : 0.);
+				else if (lt == tank)
+					money_cost += ( std::min(weap_dmg * in_rate, currLife)
+					              * static_cast<double>(env.scoreSelfHit) )
+					            + ( (weap_dmg * in_rate) >= currLife
+					                ? static_cast<double>(env.scoreUnitSelfDestroy)
+					                : 0.);
+				else
+					money_cost += ( std::min(weap_dmg * in_rate, op->opLife)
+					              * static_cast<double>(env.scoreTeamHit) )
+					            + ( (weap_dmg * in_rate) >= op->opLife
+					                ? static_cast<double>(env.scoreUnitSelfDestroy)
+					                : 0.);
+			} // End of opponent in explosion
+
+			op = op->next;
+		} // End of looping opponents memory
+	} // end of calculating splash damage score
+
+
+	/* -------------------------------------------------------------
+	 * --- 6) Kamikaze potential                                 ---
+	 * --- If the bot decides to self destruct, it is important  ---
+	 * --- to check what this weapon would do.                   ---
+	 * ------------------------------------------------------------- */
+    double selfde_score = 0.;
+    if ( (mem_curr->opLife > (currLife * 10.))
+	  || (isShocked && (mem_curr->opLife > (currLife *  5.))) ) {
+
+		// Only some weapons are considered for a big boom bye bye
+		if ( ( (SML_NUKE   <= pWeap->type) && (DTH_HEAD   >= pWeap->type) )
+		  || ( (WIDE_BOY   == pWeap->type) || (CUTTER     == pWeap->type) )
+		  || ( (MED_NAPALM == pWeap->type) || (LRG_NAPALM == pWeap->type) ) ) {
+			double kRad = pWeap->radius;
+			double kDmg = pWeap->dmgSingle;
+
+			// for a kamikaze, the shaped weapons have to be shot somewhat to
+			// the side, so extend the radius if this tank is not buried, or
+			// reduce it to zero if it is.
+			if ((WIDE_BOY == pWeap->type) || (CUTTER == pWeap->type)) {
+				if (buried >= (BURIED_LEVEL / ai_level))
+					kRad = 0.;
+				else
+					kRad += 50. * ai_over_mod;
+			}
+
+			// The same counts for the napalm, although it does not really have
+			// a radius. This must be estimated according to the current wind.
+			else if ((MED_NAPALM == pWeap->type) || (LRG_NAPALM == pWeap->type)) {
+				if (buried >= (BURIED_LEVEL / ai_level))
+					kRad = 0.;
+				else {
+					kRad = std::abs(global.wind / (env.windstrength / 4.)) + 1.;
+					/* This produces the following multiplier: (with max wind = 8)
+					 * wind = 0 : (0 / (8 / 4)) + 1 = (0 / 2) + 1 = = 1
+					 * wind = 1 : (1 / (8 / 4)) + 1 = (1 / 2) + 1 = = 1.5
+					 * wind = 4 : (4 / (8 / 4)) + 1 = (4 / 2) + 1 = = 3
+					 * wind = 6 : (6 / (8 / 4)) + 1 = (6 / 2) + 1 = = 4
+					 * wind = 8 : (8 / (8 / 4)) + 1 = (8 / 2) + 1 = = 5
+					*/
+					kRad *= weapon[pWeap->type].launchSpeed / ai_level;
+
+					// Napalm is a cluster, but not everything will hit
+					kDmg = pWeap->dmgCluster / ai_level_d;
+				}
+			}
+
+			// Check against collateral damage unless shocked and the shocker is
+			// in range
+			bool tgt_in_range  = false;
+			if ( isShocked
+			  && (mem_curr->entry == shocker) // can this be false?
+			  && (mem_curr->distance < kRad) ) {
+				// No check, just do it
+				selfde_score = (pWeap->dmgCluster + pWeap->dmgSingle) * ai_over_mod;
+				tgt_in_range = true;
+			} else {
+				// Nope, be reasonable
+				selfde_score        = kDmg;
+				sOppMemEntry* check = mem_head;
+				while (check) {
+
+					// Yield on each iteration to not hog the CPUs
+					if (!global.skippingComputerPlay)
+						std::this_thread::yield();
+
+					if ( (check->opLife > 0.) && (check->distance < kRad) ) {
+						if (check->onSameTeam)
+							selfde_score -= kDmg * ai_type_mod
+							              * ( 1.25 + player->defensive);
+						else
+							selfde_score += kDmg * ai_over_mod * check->team_mod;
+
+						// Award extra points if this is the current target
+						if (mem_curr == check) {
+							selfde_score += check->opLife * check->team_mod;
+							tgt_in_range  = true;
+						}
+					}
+					check = check->next;
+                }
+			} // End of checking done damage
+
+			// Now, if the score is positive, this is a kamikaze choice:
+			if ( (selfde_score > 0.) && tgt_in_range)
+				pWeap->kamikaze = true;
+
+		} else
+			// Unsuitable
+			selfde_score -= pWeap->dmgSpread * ai_type_mod;
+	  }
+
+
+	/* -------------------------------------------------------------
+	 * --- 7) Economic evaluation                                ---
+	 * --- If the maximum damage bounty the weapon can generate  ---
+	 * --- is lower than the weapon score, points are docked.    ---
+	 * --- Generating more money than the weapon is worth adds   ---
+	 * --- some bonus points.                                    ---
+	 * ------------------------------------------------------------- */
+	double eco_score  = 0.;
+	double money_mod  = needMoney
+	                  ? ai_type_mod
+	                  : (static_cast<double>(RAND_AI_1P + 1) * 10.);
+	double money_diff = money_made - money_cost;
+	if (!isShocked) {
+		// Note: Shocked bots do not care about money!
+		if ( money_diff >= weapon[pWeap->type].cost)
+			eco_score += (money_made / money_mod)
+			           - (money_cost / money_mod);
+		else
+			eco_score -= money_diff / money_mod;
+	}
+
+
+	/* -------------------------------------------------------------
+	 * --- 8) Sum up the score                                   ---
+	 * --- This will be used for sorting the weapons list        ---
+	 * ------------------------------------------------------------- */
+	double pref_score = pWeap->preference / ai_level_d
+	                  / (std::abs(dmg_diff) > 1. ? std::abs(dmg_diff) : 1.);
+	                  // the further away, the less likely.
+
+	DEBUG_LOG_EMO(player->getName(), "  preference   : %6.2lf", pref_score)
+	DEBUG_LOG_EMO(player->getName(), "  point_score  : %6.2lf [diff %6.2lf]",
+	              point_score, dmg_diff)
+	DEBUG_LOG_EMO(player->getName(), "  unbury_score : %6.2lf", unbury_score)
+	DEBUG_LOG_EMO(player->getName(), "  panic_score  : %6.2lf", panic_score)
+	DEBUG_LOG_EMO(player->getName(), "  splash_score : %6.2lf", splash_score)
+	DEBUG_LOG_EMO(player->getName(), "  selfde_score : %6.2lf", selfde_score)
+	DEBUG_LOG_EMO(player->getName(), "  dig_score    : %6.2lf", dig_score)
+	DEBUG_LOG_EMO(player->getName(), "  eco_score    : %6.2lf (M %6.2lf / C -%6.2lf / D %6.2lf)",
+	              eco_score, money_made, money_cost, money_diff)
+
+	double xScore = pref_score + point_score + unbury_score + panic_score
+	              + splash_score + selfde_score + dig_score + eco_score;
+
+	pWeap->score = ROUND(xScore);
+
+	DEBUG_LOG_EMO(player->getName(), "  Final Score  : %8d", pWeap->score)
+}
+
+
+/** @brief Select a tool to free the tank or clear a path
+  * @param[in] free_tank If set to true, a tool to free the tank is chosen,
+  *            a tool to clear the path otherwise.
+  * @param[in] is_last If set to true, an emergency selection is done to force
+  *            this method to succeed.
+  * @return true if the selection succeeded, false otherwise.
+**/
+bool AICore::useFreeingTool(bool free_tank, bool is_last)
+{
+	// If the current weapon is already used to blast out
+	// an opponent, no other tool is needed.
+	if (!free_tank && weap_curr && weap_curr->blastOut)
+		return true;
+
+	if ( ( free_tank
+		&& ( useWeapon(RIOT_BLAST)
+		  || useWeapon(RIOT_CHARGE) ) )
+	  || useWeapon(HVY_RIOT_BOMB)
+	  || useWeapon(RIOT_BOMB)
+	  || (!free_tank
+		&& ( useWeapon(CHAIN_GUN)
+		  || useWeapon(DRILLER)
+		  || useWeapon(CHAIN_MISSILE) ) )
+	  || (free_tank
+	    && ( useItem(ITEM_TELEPORT) // Note: No mass teleport here!
+	      || ( !mem_curr->is_buried
+	        && useItem(ITEM_SWAPPER) ) ) ) ) {
+
+		DEBUG_LOG_AIM(player->getName(), "Selected %s to %s",
+		              weap_idx < WEAPONS
+		              ? weapon[weap_idx].getName()
+		              : item[weap_idx - WEAPONS].getName(),
+		              free_tank ? "free my tank" : "clear firing path")
+
+		return true;
+	}
+
+	// If the "normal" selection is not possible (out of stock)
+	// but this is the last try, the bot has to revert to standard
+	// missiles. Expensive, but should work.
+	else if ( is_last
+	  && ( ( !free_tank
+		  && ( useWeapon(SML_NUKE)
+			|| useWeapon(LRG_MIS)
+			|| useWeapon(MED_MIS) ) )
+	    || useItem(ITEM_TELEPORT)
+	    || ( useItem(ITEM_SWAPPER)
+	      && !mem_curr->is_buried )
+	    || useItem(ITEM_MASS_TELEPORT) // As a last resort this is okay.
+		|| useWeapon(SML_MIS) ) ) {
+
+		DEBUG_LOG_AIM(player->getName(), "(LAST) Selected %s to %s",
+		              weap_idx < WEAPONS
+		              ? weapon[weap_idx].getName()
+		              : item[weap_idx - WEAPONS].getName(),
+		              free_tank ? "free my tank" : "clear firing path")
+
+		return true;
+	}
+
+	return is_last;
+}
+
+
+/// @brief explicitly select @a item_type, returns true if available and chosen.
+bool AICore::useItem (itemType item_type)
+{
+	if (env.isItemAvailable(item_type) && (player->ni[item_type] > 0)) {
+		item_curr = item_head;
+		while (item_curr && (item_curr->type != item_type))
+			item_curr = item_curr->next;
+
+		if (item_curr && (item_curr->type == item_type)) {
+			weap_idx  = WEAPONS + item_type;
+			weap_curr = nullptr;
+			return true;
+		} else if (weap_curr)
+			item_curr = nullptr;
+	}
+
+	return false;
+}
+
+
+/// @brief convenience function to use the full index as an integer to choose
+/// an item. Full index means the value is beyond the WEAPONS constant.
+bool AICore::useItem(int32_t item_index)
+{
+	if ( (item_index >= WEAPONS) && (item_index < THINGS) )
+		return useItem(static_cast<itemType>(item_index - WEAPONS));
+	return false;
+}
+
+
+/// @brief explicitly select @a weapon_type, returns true if available and chosen.
+bool AICore::useWeapon (weaponType weap_type)
+{
+	if (env.isItemAvailable(weap_type) && (player->nm[weap_type] > 0)) {
+		weap_curr = weap_head;
+		while (weap_curr && (weap_curr->type != weap_type))
+			weap_curr = weap_curr->next;
+
+		if (weap_curr && (weap_curr->type == weap_type)) {
+			weap_idx = weap_type;
+			item_curr = nullptr;
+			return true;
+		} else if (item_curr)
+			weap_curr = nullptr;
+	}
+
+	return false;
+}
+
+
+/// @brief convenience function to use the numeric index as an integer to choose
+/// a weapon.
+bool AICore::useWeapon(int32_t weap_index)
+{
+	if (weap_index < WEAPONS)
+		return useWeapon(static_cast<weaponType>(weap_index));
+	return false;
+}
+
+
+/// @brief Call this once the AI weapon is fired to signal the end of the
+/// players turn
+void AICore::weapon_fired()
+{
+	if (isWorking && !isStopped && (PS_FIRE == plStage)) {
+		DEBUG_LOG_AI(player->getName(), "------------------------------", 0)
+		DEBUG_LOG_AI(player->getName(), " Weapon fired for %s", player->getName())
+
+		lguard_t guard(actionMutex);
+		plStage = PS_CLEANUP;
+		actionCondition.notify_one();
+	}
+}
+
+
+/// @brief Core threading operator
+void AICore::operator()()
+{
+	while (canWork && !isStopped) {
+
+		// Go to sleep until the thread is woken up
+		luniq_t actionLock(actionMutex);
+		actionCondition.wait(actionLock, [this]{
+			return (isWorking || isStopped);
+		} );
+
+		// If the thread is to be stopped, exit the loop
+		if (isStopped)
+			// Cleaner than "break", but only on a philosophical level... ;-)
+			continue;
+
+		if (!initialize()) {
+			plStage   = PS_AI_IS_IDLE;
+			isWorking = false;
+			continue;
+		}
+
+		// --------------------------------------------------------------------
+		// --- First update the foe list, only then a target can be picked- ---
+		// --------------------------------------------------------------------
+		checkOppMem();
+
+
+		// -----------------------------------------------------------------
+		// --- See whether the bot falls for a fear shock.               ---
+		// --- If they are mortally afraid of a shocker, no other target ---
+		// --- will be picked. It is fixed on that one then.             ---
+		// -----------------------------------------------------------------
+		if (shocker) {
+			DEBUG_LOG_EMO(player->getName(),
+			              "Terrified by %s (fear shock: %lf)",
+			              shocker->opponent->getName(), shocker->fear_shock)
+			double reshock = shocker->fear
+			               - (static_cast<double>(RAND_AI_0P + 2) / 2.);
+			if (reshock >= shocker->fear_shock) {
+				isShocked = true;
+
+				// Generate a nice message telling the world that we are in awe:
+				if (!isStopped && !global.skippingComputerPlay) {
+					const char* text = player->selectPanicPhrase(shocker->opponent);
+					try {
+						if (text) {
+							// Wait for the AI to be allowed to create texts
+							while (!textAllowed.load(ATOMIC_READ))
+								std::this_thread::yield();
+
+							// Now create the instance
+							new FLOATTEXT(text,x, y - 30, .0, -.4, player->color,
+										  CENTRE, TS_NO_SWAY, 150);
+						}
+					} catch (...) {
+						perror ( "aicore.cpp: Failed to allocate memory for"
+								 " panic text in operator().");
+					}
+					if (text)
+						free(const_cast<char*>(text));
+				}
+
+
+				DEBUG_LOG_EMO(player->getName(),
+				              "Shock confirmed with %lf over %lf",
+				              reshock, shocker->fear_shock)
+			} else {
+				isShocked = false;
+				DEBUG_LOG_EMO(player->getName(),
+				              "Overcame shock with %lf under %lf",
+				              reshock, shocker->fear_shock)
+			}
+		}
+
+
+		// ---------------------------------------------------
+		// --- Set basic behaviour values                  ---
+		// --- Done here and not in initialize so the full ---
+		// --- shock check is already done.                ---
+		// ---------------------------------------------------
+		findOppAttempts = ai_level + 2 - (isShocked ? ai_level / 2 : 0);
+		findRngAttempts = (std::pow(ai_level + 1, 2) + 1)
+		                / (isShocked ? ai_level + 1 : 1)
+		                + (isShocked ? 0 : ai_level + 4);
+		findTgtAttempts = ai_level
+		                - (isShocked ? ai_level - 1 : 0)
+		                + (isShocked ? 0 : 1);
+		findWeapAttempts= ai_level * 2 / (isShocked ? ai_level : 1);
+		focusRate       = ai_level_d * 2.
+		                / static_cast<double>(maxAiLevel * 2);
+		errorMultiplier = static_cast<double>(maxAiLevel + 1 - ai_level)
+		                / static_cast<double>(findRngAttempts);
+		maxBounce       = ROUNDu(std::pow(ai_level, 2) / 2.) + 2;
+		/* The results should be [if shocked]:
+		 * findOppAttempts : Useless   3   [2], Deadly + 1:  8    [4]
+		 * findRngAttempts : Useless: 10   [2], Deadly + 1: 60    [7]
+		 * findTgtAttempts : Useless:  2   [1], Deadly + 1:  7    [1]
+		 * findWeapAttempts: Useless:  2   [1], Deadly + 1: 12    [2]
+		 * focusRate       : Useless:  0.166,   Deadly + 1:  1.0
+		 * errorMultiplier : Useless:  1.2 [3], Deadly + 1:  0.02 [0.14]
+		 * maxBounce       : Useless:  3,       Deadly + 1: 20
+		 */
+
+		DEBUG_LOG_AI(player->getName(), "AI Level       : %d (%s)", ai_level,
+					 getLevelName(ai_level))
+		DEBUG_LOG_AI(player->getName(), "type_mod       : %4.3lf", type_mod)
+		DEBUG_LOG_AI(player->getName(), "errorMultiplier: %4.3lf",
+					 errorMultiplier)
+		DEBUG_LOG_AI(player->getName(), "findOppAttempts: %d", findOppAttempts)
+		DEBUG_LOG_AI(player->getName(), "findRngAttempts: %d", findRngAttempts)
+		DEBUG_LOG_AI(player->getName(), "findTgtAttempts: %d", findTgtAttempts)
+		DEBUG_LOG_AI(player->getName(), "focusRate      : %4.3lf", focusRate)
+		DEBUG_LOG_AI(player->getName(), "maxBounce      : %d", maxBounce)
+		DEBUG_LOG_AI(player->getName(), "needMoney      : %s",
+					 needMoney ? "Yes" : "No")
+
+
+		// ------------------------------------------------------------------
+		// --- The full cycle of target selection, weapon/item selection, ---
+		// --- setting up the basic combat values and targeting the       ---
+		// --- selected weapon might need a few attempts. The higher the  ---
+		// --- AI level, the more attempts the bot gets. If the maximum   ---
+		// --- number of attempts is reached, all used methods are forced ---
+		// --- to come up with a minimum result.                          ---
+		// ------------------------------------------------------------------
+		int32_t tgt_attempts  = 0;
+		int32_t opp_attempts  = 0;
+		int32_t weap_attempts = 0;
+		bool    done          = false;
+
+		while (canWork && isWorking && !isStopped
+		    && (needAim || !isBlocked) // End if a free is needed
+			&& (tgt_attempts < findTgtAttempts) ) {
+
+			// Yield on each iteration to not hog the CPUs
+			if (!global.skippingComputerPlay)
+				std::this_thread::yield();
+
+			// ----------------------------------------------------------
+			// --- 1) Cycle target and item selection.                ---
+			// --- Those are combined, because selecting a different  ---
+			// --- target later might make the current item selection ---
+			// --- less effective or even useless. Thus the item is   ---
+			// --- chosen individually.                               ---
+			// ----------------------------------------------------------
+			if (!opp_attempts && !weap_attempts) {
+				++tgt_attempts;
+				mem_curr  = nullptr;
+				DEBUG_LOG_AIM(player->getName(), "Starting setup %d / %d",
+				              tgt_attempts, findTgtAttempts)
+			}
+			done = setupAttack(tgt_attempts == findTgtAttempts,
+			                   opp_attempts, weap_attempts);
+
+			// ----------------------------------------------------------
+			// --- 2) Calculate basic attack values.                  ---
+			// --- If the target and item selection is different than ---
+			// --- in the last round, new basic values must be        ---
+			// --- calculated. If the selections are what this player ---
+			// --- had in the last round, this won't be needed. Just  ---
+			// --- continue were we left off last round.              ---
+			// ----------------------------------------------------------
+			if (done)
+				done = calcAttack(tgt_attempts);
+
+			// ----------------------------------------------------------
+			// --- 3) Aim the current selection                       ---
+			// ----------------------------------------------------------
+			if (done && needAim && !isBlocked)
+				done = aim( (tgt_attempts == findTgtAttempts) && needSuccess );
+			else  if (!needAim || isBlocked) {
+				DEBUG_LOG_AIM(player->getName(), "No aiming done: %s, %s",
+				              needAim   ? "Aiming needed"   : "Aiming NOT needed",
+				              isBlocked ? "shot is blocked" : "Shot is NOT blocked")
+			}
+
+
+			// ------------------------------------------------------
+			// --- 4) If this round was successful, check whether ---
+			// ---    A new best setup is found                   ---
+			// ------------------------------------------------------
+			if (done) {
+				// Reset opponent and weapon attempts if a positive score
+				// was achieved and the AI has tried enough items or the
+				// opponent selection is finished.
+				if (best_round_score > 0) {
+					if ( (weap_attempts > ai_level)
+					  || (opp_attempts == findOppAttempts) ) {
+						opp_attempts  = 0;
+						weap_attempts = 0;
+					}
+
+					// Tweak the score if the primary target was hit:
+					if (best_prime_hit) {
+
+						// add the weapon and opponent score, so attacks, even
+						// if they are not perfect, get emphasized if the preferred
+						// setup is chosen:
+						if (  mem_curr && revengee
+						  && (player != mem_curr->entry->opponent) )
+							best_round_score += mem_curr->score
+											  / (revengee == mem_curr->entry
+												 ? ai_level : ai_level * 10);
+						if (weap_curr && (weap_curr->dmgSingle > 0) )
+							best_round_score += weap_curr->score / (ai_level * 10);
+					}
+				} // End of having a best_round_score greater than zero
+
+				// Note down best setup score and settings if better or
+				// forced to succeed due to last attempt condition
+				bool new_best_setup_score = (best_round_score > best_setup_score);
+				if ( ( ( new_best_setup_score && best_prime_hit)
+				    || ( !best_setup_prime
+				      && (new_best_setup_score || best_prime_hit) ) )
+				  || ( (NEUTRAL_ROUND_SCORE == best_setup_score)
+					&& (tgt_attempts == findTgtAttempts)
+				    && needSuccess) ) {
+					best_setup_angle     = curr_angle;
+					best_setup_item      = item_curr;
+					best_setup_mem       = mem_curr;
+					best_setup_overshoot = best_overshoot;
+					best_setup_power     = curr_power;
+					best_setup_prime     = best_prime_hit;
+					best_setup_weap      = weap_curr;
+					DEBUG_LOG_AIM(player->getName(),
+								"New best setup with angle %d, power %d using %s : (%d > %d)",
+								GET_DISP_ANGLE(curr_angle), curr_power,
+								weap_idx < WEAPONS
+								? weapon[weap_idx].getName()
+								: item[weap_idx - WEAPONS].getName(),
+								best_round_score, best_setup_score)
+					best_setup_score = best_round_score;
+
+					// This targeting round is definitely over
+					opp_attempts  = 0;
+					weap_attempts = 0;
+
+					if ( needSuccess
+					  && best_setup_prime
+					  && (best_setup_score > 0) )
+						// There is no need to force anything any more:
+						needSuccess = false;
+
+					// Give feedback according to what has happened
+					if ( (best_round_score > 0) && best_prime_hit )
+						showFeedback("!!!", GREEN, -.5, TS_NO_SWAY, 150);
+					else
+						showFeedback("!", GREEN, -.6, TS_HORIZONTAL, 120);
+				} else if (needSuccess)
+					showFeedback("?", RED, -.7, TS_HORIZONTAL, 90);
+			} // End of setup score handling
+		} // End of full preparation cycle
+
+
+		// ---------------------------------------------------------
+		// --- If the revengee has been changed due to the score ---
+		// --- considerations, write back the new victim:        ---
+		// ---------------------------------------------------------
+		if (revengee && (revengee->opponent != player->revenge)) {
+			if (revengee->opponent != player)
+				player->revenge = revengee->opponent;
+			else
+				revengee = nullptr;
+		} else if (!revengee)
+			player->revenge = nullptr;
+
+
+		// --------------------------------------------
+		// --- If no real setup could be found, see ---
+		// --- whether a freeing attempt is needed. ---
+		// --------------------------------------------
+		if (!isStopped && !isShocked
+		  && !isBlocked  // If these fail, aim() already has set up
+		  && needAim     // a freeing attempt. Do not do it twice!
+		  && best_setup_weap
+		  && ( (best_setup_score < 0)
+		    || ( (0 == best_setup_score ) && RAND_AI_0P) ) ) {
+			DEBUG_LOG_AIM(player->getName(), "Best setup score %d too low!",
+			              best_setup_score)
+
+			// First, copy best noted data (if any)
+			if (NEUTRAL_ROUND_SCORE != best_setup_score) {
+				curr_angle = best_setup_angle;
+				curr_power = best_setup_power;
+			}
+
+			// Now see whether to unbury or clear the path:
+			if ( ( buried_l >= (BURIED_LEVEL_HALF / 2) )
+			  || ( buried_r >= (BURIED_LEVEL_HALF / 2) ) ) {
+				useFreeingTool(true, true);
+				calcUnbury(true);
+			} else {
+				curr_angle = best_setup_angle;
+				curr_power = best_setup_power;
+				if (RAND_AI_0P
+				  || hill_detected
+				  || !best_setup_weap
+				  || (best_setup_weap->spread > 1)
+				  || (best_setup_weap->subMunCount > 0)
+				  || (REDUCER == best_setup_weap->type)
+				  || (PERCENT_BOMB == best_setup_weap->type) ) {
+					useFreeingTool(false, true);
+
+					// If this is a riot bomb, flatten the angle,
+					// but only if the best overshoot (we took the
+					// angle and power from its setup) was too long.
+					// No use in firing a riot bomb behind the opponent
+					if (best_setup_overshoot > 0)
+						flattenCurrAng();
+				} else {
+					// In this case use the current weapon, but go a bit down
+					// with the angle:
+					int32_t ang_mod = 5 + RAND_AI_1P;
+
+					if (curr_angle < 180) {
+						curr_angle -= ang_mod;
+						if (curr_angle < 95)
+							curr_angle = 95;
+					} else {
+						curr_angle += ang_mod;
+						if (curr_angle > 265)
+							curr_angle = 265;
+					}
+
+					if ( (REDUCER      != best_setup_weap->type)
+					  && (PERCENT_BOMB != best_setup_weap->type)
+					  && ( (RIOT_BOMB  >  best_setup_weap->type)
+					    || (RIOT_BLAST <  best_setup_weap->type) ) )
+						useWeapon(best_setup_weap->type);
+					else
+						useWeapon(SML_MIS);
+				} // end of using best setup weapon.
+			}
+
+			sanitizeCurr();
+			angle           = curr_angle;
+			power           = curr_power;
+			needAim         = false;
+			best_setup_weap = nullptr;
+			isBlocked       = true;
+
+			showFeedback("???", PURPLE, -.6, TS_NO_SWAY, 100);
+		}
+
+
+		// ----------------------------------------
+		// --- Write back the best attack setup ---
+		// ----------------------------------------
+		if (!isStopped && needAim && !isBlocked ) {
+			curr_angle = best_setup_angle;
+			curr_power = best_setup_power;
+			item_curr  = best_setup_weap ? nullptr : best_setup_item;
+			mem_curr   = best_setup_mem;
+			weap_curr  = item_curr ? nullptr : best_setup_weap;
+			weap_idx   = weap_curr ? weap_curr->type
+			           : item_curr ? item_curr->type + WEAPONS
+			           : 0;
+
+			sanitizeCurr();
+			angle      = curr_angle;
+			power      = curr_power;
+
+			DEBUG_LOG_AIM(player->getName(),
+						"Using best setup with angle %d, power %d using %s (Score %d)",
+						GET_DISP_ANGLE(angle), power,
+						weap_idx < WEAPONS
+						? weapon[weap_idx].getName()
+						: item[weap_idx - WEAPONS].getName(),
+						best_setup_score)
+		} else if (!isStopped) {
+			// Note: Without aiming or when blocked, the setup memory
+			//       was not used in some cases.
+			curr_angle = angle;
+			curr_power = power;
+			sanitizeCurr();
+			angle = curr_angle;
+			power = curr_power;
+		}
+
+
+		// ------------------------------------------------
+		// --- If a weapon is chosen and the target is  ---
+		// --- the revengee, get a message out for them ---
+		// ------------------------------------------------
+		if (!isStopped && weap_curr && revengee && needAim
+		  && (revengee == best_setup_mem->entry)
+		  && best_setup_prime && !needSuccess
+		  && !global.skippingComputerPlay
+		  && (weap_curr->dmgSingle > 1.)
+		  && (weap_curr->dmgSingle >= (mem_curr->opLife * ai_over_mod / 2.))
+		  && RAND_AI_1P) {
+			const char* text = player->selectRetaliationPhrase();
+			try {
+				if (text) {
+					// Wait for the AI to be allowed to create texts
+					while (!textAllowed.load(ATOMIC_READ))
+						std::this_thread::yield();
+
+					// Now create the instance
+					new FLOATTEXT(text,x, y - 30, .0, -.4, player->color,
+								  CENTRE, TS_NO_SWAY, 150);
+				}
+			} catch (...) {
+				perror ( "aicore.cpp: Failed to allocate memory for"
+						 " retaliation text in operator().");
+			}
+			if (text)
+				free(const_cast<char*>(text));
+		}
+
+
+		// -------------------------------------------------
+		// --- Tell the world this tank is going bye bye ---
+		// -------------------------------------------------
+		if ( !isStopped
+		  && (mem_curr->entry->opponent == player)
+		  && !global.skippingComputerPlay) {
+			try {
+				// Wait for the AI to be allowed to create texts
+				while (!textAllowed.load(ATOMIC_READ))
+					std::this_thread::yield();
+
+				// Now create it
+				new FLOATTEXT (player->selectKamikazePhrase(),
+				               x, y - 30, .0, -.4,
+				               player->color, CENTRE, TS_NO_SWAY, 300);
+			} catch (...) {
+				perror ( "aicore.cpp: Failed allocating memory for"
+				         " kamikazeText in operator().");
+			}
+		}
+
+
+		// ---------------------------------------
+		// --- Apply some "last second" errors ---
+		// ---------------------------------------
+		if (!isStopped && needAim && !isBlocked
+		     // Assume that bots can 'fix' errors from the last round:
+		  && ( (nullptr == mem_curr) || (mem_curr->entry != last_opp) )
+		  && RAND_AI_1N) {
+			double ang_err = (rand() %  8) * errorMultiplier;
+			double pow_err = (rand() % 36) * errorMultiplier;
+
+			// Angles always go 'up', but never over the top
+			if (angle > (180. + ang_err))
+				curr_angle = angle - ang_err;
+			else if (angle < (180. - ang_err))
+				curr_angle = angle + ang_err;
+
+			// Power error is always a raise
+			curr_power = power + pow_err;
+
+			sanitizeCurr();
+
+			DEBUG_LOG_AIM(player->getName(),
+					"Last second errors: Angle %d° -> %d°), Power: %d -> %d)",
+					GET_DISP_ANGLE(angle), GET_DISP_ANGLE(curr_angle),
+					power, curr_power)
+
+			showFeedback("*fumble*", RED, -.8, TS_NO_SWAY, 100);
+
+			angle = curr_angle;
+			power = curr_power;
+
+		}
+		assert( (angle == curr_angle) && "ERROR: Finished but angle not set!");
+		assert( (power == (curr_power - (curr_power % 5)))
+				&& "ERROR: Finished but power not set!");
+		assert( ( (weap_idx >= WEAPONS) || (0 == weapon[weap_idx].warhead) )
+		     && "ERROR: Not usable warhead chosen!");
+		assert( (weap_idx >= 0) && (weap_idx < THINGS)
+		     && env.isItemAvailable(weap_idx)
+		     && "ERROR: Unavailable or invalid weap_idx!");
+		assert( ( (weap_idx >= WEAPONS) || (player->nm[weap_idx] > 0) )
+		     && "ERROR: Weapon chosen that is out of stock!");
+		assert( ( (weap_idx < WEAPONS) || (player->ni[weap_idx - WEAPONS] > 0) )
+		     && "ERROR: Item chosen that is out of stock!");
+		assert( ( (weap_idx < WEAPONS)
+		       || ((weap_idx - WEAPONS) < ITEM_LGT_SHIELD)
+		       || ((weap_idx - WEAPONS) == ITEM_FUEL)
+		       || ((weap_idx - WEAPONS) == ITEM_ROCKET) )
+		     && "ERROR: The chosen item is not usable!");
+
+
+		// ---------------------------------------
+		// --- Wait for the weapon to be fired ---
+		// ---------------------------------------
+		if (!isStopped) {
+			plStage = PS_FIRE; // It can be fired now
+
+			DEBUG_LOG_AI(player->getName(),
+			             "Finished thinking, waiting to fire %s against %s",
+			             weap_curr
+			             ? weapon[weap_idx].getName()
+			             : item[weap_idx - WEAPONS].getName(),
+			             mem_curr
+			             ? mem_curr->entry->opponent->getName()
+			             : "Nobody")
+
+			actionCondition.wait(actionLock, [this]{
+				return (!canWork || !isWorking || isStopped
+						|| (PS_CLEANUP == plStage) );
+			} );
+		}
+
+
+		// --------------------------------------
+		// --- Remember the current selection ---
+		// --- (But only if it was hit)       ---
+		// --------------------------------------
+		if (!isStopped && best_setup_prime && (best_setup_mem == mem_curr) )
+			player->setLastOpponent(mem_curr ? mem_curr->entry : nullptr);
+		else
+			player->setLastOpponent(nullptr);
+
+		DEBUG_LOG_AI(player->getName(), "Cleaning up...", 0)
+
+		// --------------------
+		// ---   Clean up   ---
+		// --------------------
+		angle          = 180;
+		power          = MAX_POWER / 2;
+		curr_angle     = 180;
+		curr_power     = MAX_POWER / 2;
+		curr_overshoot = MAX_OVERSHOOT;
+		weap_idx       = SML_MIS;
+		blast_big      = 0.;
+		blast_max      = 0.;
+		blast_med      = 0.;
+		blast_min      = 0.;
+		textAllowed.store(false, ATOMIC_WRITE);
+
+		// Note: There is no need to clean up the memory chain.
+		// It is only created once, all players have the same
+		// size, and the getMemory() method reuses an existing
+		// one.
+		player    = nullptr;
+		tank      = nullptr;
+
+		// Eventually signal that the work has finished.
+		plStage   = PS_AI_IS_IDLE;
+		isWorking = false;
+	} // End of not being stopped
+
+	isFinished = true;
+}
+
+
+/// =========================================
+/// === Helper list entry implementations ===
+/// =========================================
+
+
+/// @brief explicit constructor adding the instance to the list
+sItemListEntry::sItemListEntry(sItemListEntry* prev_) :
+	prev(prev_)
+{
+	if (prev) {
+		next = prev->next;
+		prev->next = this;
+
+		if (next)
+			next->prev = this;
+	}
+}
+
+
+/// @brief The destructor removes the element from the list
+sItemListEntry::~sItemListEntry()
+{
+	if (prev) {
+		prev->next = next;
+		prev       = nullptr;
+	}
+	if (next) {
+		next->prev = prev;
+		next       = nullptr;
+	}
+}
+
+
+/// @brief explicit constructor adding the instance to the list
+sOppMemEntry::sOppMemEntry(sOppMemEntry* prev_) :
+	prev(prev_)
+{
+	if (prev) {
+		next = prev->next;
+		prev->next = this;
+
+		if (next)
+			next->prev = this;
+	}
+}
+
+
+/// @brief The destructor removes the element from the list
+sOppMemEntry::~sOppMemEntry()
+{
+	if (prev) {
+		prev->next = next;
+		prev       = nullptr;
+	}
+	if (next) {
+		next->prev = prev;
+		next       = nullptr;
+	}
+	entry = nullptr;
+}
+
+
+/// @brief explicit constructor adding the instance to the list
+sWeapListEntry::sWeapListEntry(sWeapListEntry* prev_) :
+	prev(prev_)
+{
+	if (prev) {
+		next = prev->next;
+		prev->next = this;
+
+		if (next)
+			next->prev = this;
+	}
+}
+
+
+/// @brief The destructor removes the element from the list
+sWeapListEntry::~sWeapListEntry()
+{
+	if (prev) {
+		prev->next = next;
+		prev       = nullptr;
+	}
+	if (next) {
+		next->prev = prev;
+		next       = nullptr;
+	}
+}
+
+
diff --git a/src/aicore.h b/src/aicore.h
new file mode 100644
index 0000000..f99eff9
--- /dev/null
+++ b/src/aicore.h
@@ -0,0 +1,950 @@
+#pragma once
+#ifndef ATANKS_SRC_AICORE_H_INCLUDED
+#define ATANKS_SRC_AICORE_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ * Copyright (C) 2003  Thomas Hudson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/** @file aicore.h
+  * @brief Home of the AICore class
+  *
+  * This class substituted the old AI code in the PLAYER class.
+  *
+  * While the old AI worked pretty well in most situations, it did
+  * lag the game on more intense calculations.
+  * Further there were some situations (like peaks in the way and
+  * such things) that the old AI could not handle.
+  * It could have been fixed, but would have lagged the game even more.
+  *
+  * By outsourcing the AI to its own class and providing an
+  * operator()(), the AI calculations could be put in a background
+  * thread. This resulted in a much smoother gaming experience.
+  *
+  * Further the AI is now easier to debug, to maintain and to extend
+  * than it was possible with the old AI code.
+**/
+
+
+#include "player_types.h"
+#include "globaltypes.h"
+#include "floattext.h"
+
+#include <condition_variable>
+
+// A few often needed randomization shortcuts
+#define RAND_AI_0P       (rand() % ai_level)
+#define RAND_AI_0N (0 == (rand() % ai_level))
+#define RAND_AI_1P       (rand() % (ai_level + 1))
+#define RAND_AI_1N (0 == (rand() % (ai_level + 1)))
+
+// Init and check for round scores
+#define NEUTRAL_ROUND_SCORE -1000000
+
+#ifndef HAS_PLAYER
+class PLAYER;
+struct sOpponent;
+#endif // HAS_PLAYER
+class TANK;
+
+// These are restricted to aicore.cpp, as
+// they are of no use anywhere else. - sed
+struct sItemListEntry;
+struct sOppMemEntry;
+struct sWeapListEntry;
+
+
+/** @class AICore
+  * @brief Core AI, written to be used as a background thread.
+  *
+  * The AI operator() is the main function that does all the work.
+  *
+  * If you need to edit something in that work flow, please try to
+  * keep the documentation up-to-date.
+  *
+  * The following is the work flow in AICore::operator().
+  *
+  *
+  * Initialization in initialize():
+  * --------------------------------
+  * ai_level      : (player->type) This is meant as a short cut being a widely usable int32_t.
+  * ai_level_d    : (ai_level)     ai_level cast to double.
+  * ai_over_mod   : (from level)   Multiplier [1.1;1.5] according to ai_level_d, used to evaluate overkills and similar.
+  * ai_type_mod   : (from level)   Multiplier [1.0;3.0] according to ai_level_d, used on important decisions to
+  *                                strengthen higher bot levels.
+  * blast_*       : (0)            Damage values of the available normal missiles.
+  * isShocked     : (false)        Whether the bot is shocked by anothers massive damage they dealt.
+  * revengee      : (nullptr)      sOpponent pointer set to a bot this one wants revenge against.
+  * shocker       : (nullptr)      sOpponent pointer set to the bot that caused isShocked to become true.
+  * needSuccess   : (true)         This is set to true and later set to false if a full targetting round resulted in a
+  *                                primary target hit, or the full score is greater than zero.
+  * needAim       : (true)         Causes aim() to be called. Set to false if the bot needs to free themselves.
+  * isBlocked     : (false)        Set to true if an obstacle is detected or the bot is buried.
+  * hill_detected : (false)        Set to true if the progress in aiming suggest, that a hill is in the firing path.
+  * needMoney     : (check done)   Set to true if getMoneyToSave() returns more money than the player has.
+  * tank          : (player->tank) Short cut.
+  * angle         : (tank->a)      Written back current angle set on the tank.
+  * power         : (tank->p)      Written back current power set on the tank.
+  * weap_idx      : (tank->cw)     Written back currently chosen weapon.
+  * x             : (tank->x)      Written back current x position of the tank.
+  * y             : (tank->y)      Written back current y position of the tank.
+  * currLife      : (tank->l + sh) Both live and remaining shield strength.
+  * buried        : (check done)   Number of true angles where the turret is directly covered by dirt.
+  * buried_l      : (check done)   Same as buried, but left side only
+  * buried_r      : (check done)   Same as buried, but right side only
+  * maxLife       : (check done)   The current maxLife value of the tank for this game round.
+  *
+  * Information from last round:
+  * last_opp      : (from player)  sOpponent pointer set to the opponent that was attacked last.
+  * last_ang      : (180 / angle)  If a last_opp is set, store current tank angle, initialize with 180 otherwise.
+  * last_pow      : (1000 / power) If a last_opp is set, store current tank power, initialize with 1000 otherwise.
+  * last_weap     : (0 / weapon)   If a last_opp is set, store current tank weapon, initialize with 0 (Small Missile)
+  *                                otherwise.
+  *                                If a last weapon/item is set, it is selected for the current first evaluation, too.
+  *                                This is done so the first weapon/item selection will chose something different than
+  *                                was tried in the last round.
+  *
+  * Information for the current targetting round:
+  * curr_angle      : (angle)               The angle for the current aiming round.
+  * curr_power      : (power)               The power for the current aiming round.
+  * best_round_score: (NEUTRAL_ROUND_SCORE) Best score achieved for the current opponent.
+  *
+  * Best achieved values over the full targetting cycle:
+  * best_setup_angle     : (angle)
+  * best_setup_item      : (nullptr)
+  * best_setup_mem       : (nullptr)
+  * best_setup_overshoot : (MAX_OVERSHOOT)
+  * best_setup_power     : (power)
+  * best_setup_score     : (NEUTRAL_ROUND_SCORE)
+  * best_setup_weap      : (nullptr)
+  *
+  * Eventually a check is made to decide whether the bot gets lucky. This temporarily raises its AI level.
+  *
+  *
+  * Initialization in  checkOppMem():
+  * --------------------------------
+  * Every opponent entry is evaluated, and a (possible) current shocker and/or revengee are determined.
+  * The revengee is written back to the player if it is a new one.
+  * The opponent memory list is then sorted by score in descending order.
+  *
+  *
+  * Re-Check shocked state:
+  * --------------------------------
+  * At this point it is checked again whether the bot is really in terror. Although difficult,
+  * it is possible for a bot to overcome their shock here. This is done so the bots won't be
+  * shocked almost constantly from round 2 on.
+  *
+  *
+  * Initialization in operator():
+  * --------------------------------
+  * findTgtAttempts : Targetting attempts. Number of full cycles of finding an attack setup.
+  *                   Counted using tgt_attempts in the operator() loop.
+  * findOppAttempts : Opponent attempts. How many opponents are tried per targetting attempt.
+  *                   Counted using opp_attempts in the operator() loop.
+  * findWeapAttempts: Weapon attempts. How many weapons / items are tried per opponent attempt.
+  *                   Counted using weap_attempts in the operator() loop.
+  * findRngAttempts : Range find attempts. How many corrections to the aiming are done per weapon attempt.
+  *                   Counted using rng_attempts in aim() loop.
+  * focusRate       : This is a modifier that lessens some corrections the lower the ai level is.
+  * errorMultiplier : This is a modifier that lessens some errors the higher the ai level is.
+  * maxBounce       : The higher the AI level the more bounces/wraps from walls it can follow before
+  *                   assuming the shot has crashed.
+  *
+  * The results should be [if shocked]:
+  * findTgtAttempts : Useless:  2   [1], Deadly + 1:  7    [1]
+  * findOppAttempts : Useless   3   [2], Deadly + 1:  8    [4]
+  * findWeapAttempts: Useless:  2   [1], Deadly + 1: 12    [2]
+  * findRngAttempts : Useless: 10   [2], Deadly + 1: 60    [7]
+  * focusRate       : Useless:  0.166,   Deadly + 1:  1.0
+  * errorMultiplier : Useless:  1.2 [3], Deadly + 1:  0.02 [0.14]
+  * maxBounce       : Useless:  3,       Deadly + 1: 20
+  *
+  * As the full number of aimings can be up to Tgt*Opp*Weap*Rng attempts, the current targetting attempt
+  * is finished once a new best attack plan is found.
+  *
+  * The full cycle of target selection, weapon/item selection, setting up the basic combat values and
+  * targeting the selected weapon might need a few attempts.
+  * The higher the AI level, the more attempts the bot gets. If the maximum number of attempts is
+  * reached, all used methods are forced to come up with a minimum result.
+  *
+  * Local counters for the operator() loop:
+  * ---------------------------------------
+  * tgt_attempts  : (0)     The number of attempts to set up a succesful attack the AI already had.
+  * opp_attempts  : (0)     The number of attempts to find a suitable opponent the AI already had.
+  * weap_attempts : (0)     The number of attempts to find a suitable weapons or item the AI already had.
+  * done          : (false) Control variable set by each section method on success (true) or failure (false).
+  *
+  * Cycle in operator():
+  * --------------------------------
+  *
+  *   The loop is working until the AI can no longer work, aiming is needed or the AI is not blocked, and
+  *   tgt_attempts is lower than findTgtAttempts.
+  *
+  *   1  Cycle target and item selection if both opp_attempts and weap_attempts are both zero.
+  *      Those are combined, because selecting a different target later might make the current item selection less
+  *      effective or even useless. Thus the item is chosen individually.
+  *      tgt_attempts : (+1)      The new target and item selections marks the beginning of a new targetting attempt.
+  *      mem_curr     : (nullptr) The currently selected opponent.
+  *
+  *   2  done = setupAttack(bool is_last, int32_t &opp_attempt, int32_t &weap_attempt)
+  *
+  *        Param 1 : is_last     : is set to true if tgt_attempts equals findTgtAttempts.
+  *        Param 2 : opp_attempt : Reference to opp_attempts to have the actual opponent selection counted.
+  *        Param 3 : weap_attempt: Reference to weap_attempts to have the actual weapon/item selection counted.
+  *
+  *      Here, the basic setup is done by selecting an opponent and a suitable item
+  *      or weapon to use against them.
+  *
+  *     2.1  If either weap_attempt is 0 or mem_curr is nullptr, a new opponent selection round is started.
+  *          mem_curr can be nullptr if the previous try to find an opponent failed.
+  *          plStage     : (PS_SELECT_TARGET)
+  *          opp_attempt : (+1) Raised by one when calling selectTarget() below.
+  *
+  *       2.1.1 selectDone = selectTarget(bool is_last)
+  *               Param 1 : is_last : This is set to true if the raised opp_attempt equals findOppAttempts and is_last
+  *                                   was already set to true in setupAttack() and needSuccess is true.
+  *         2.1.1.1 If the bot is shocked, their shocker is preselected and true is returned.
+  *         2.1.1.2 If the bot has a grudge against someone, the revengee is preselected and true is returned, but only
+  *                 if either no opponent was selected yet, or the last opponent was someone else.
+  *                 However, if is_last is true, the revengee is selected even if it is two times in a row this way.
+  *         2.1.1.3 If nothing was preselected, the ordered list of opponents is simply walked down. However, if is_last
+  *                 is true, the first entry is always selected, because they are primarily wanted anyway.
+  *
+  *       2.1.2 If an opponent is selected and it is not the same as the last time one was selected, then the
+  *             following initializations are done:
+  *             item_curr   : (nullptr) The currently selected item or nullptr if weap_curr is used.
+  *             item_last   : (nullptr) The item selected in the last item selection or nullptr if none was selected.
+  *             weap_curr   : (nullptr) The currently selected weapon or nullptr if item_curr is used.
+  *             weap_last   : (nullptr) The weapon selected in the last weapon selection or nullptr if none was
+  *                                     selected. item_* and weap_* are used for weap_attempts, which are done
+  *                                     findWeapAttempts times for each opp_attempts cycle.
+  *             best_round_score : (NEUTRAL_ROUND_SCORE) A new target is always a complete new targetting round.
+  *
+  *          To make this clear:
+  *            - For each targeting attempt (tgt_attempts count to findTgtAttempts) up to findOppAttempts attempts are
+  *              granted to find a suitable opponent.
+  *            - For each opponent selection attempt (opp_attempts count to findOppAttempts) up to findWeapAttempts
+  *              attempts are granted to select a suitable item or weapon.
+  *            - For each weapon/item selection (weap_attempts count to findWeapAttempts) up to findRngAttempts attempts
+  *              are granted to the AI to actually aim the selected weapon/item on the selected opponent.
+  *
+  *     2.2  Otherwise, if both weap_attempt is greater than 0 and mem_curr is not nullptr, the current target is kept
+  *          and it is recorded that no new target was selected.
+  *
+  *     2.3  If the target selection was successful, the next item to use or weapon to fire can be selected.
+  *          plStage      : (PS_SELECT_WEAPON)
+  *          weap_attempt : (+1) Raised by one when calling selectItem() below.
+  *
+  *       2.3.1 If the current target is new, the score lists for items and weapons are regenerated.
+  *       2.3.2 At this point selectDone is set to false.
+  *       2.3.3 While selectDone is false, and the AI can work, and weap_attempt is lower than findWeapAttempts,
+  *             selectDone gets the return value of selectItem(bool is_last).
+  *               Param 1 : is_last : This is set to true if the raised weap_attempt equals findWeapAttempts and is_last
+  *                                   was already set to true in setupAttack() and needSuccess is true.
+  *
+  *         2.3.3.1 store item_curr in item_last and weap_curr in weap_last. This is needed for the regular walking
+  *                 down the ordered lists of weapons and items.
+  *         2.3.3.2 If the bot is shocked, or if it already has a best setup where the primary target was hit, a random
+  *                 weapon is chosen. This is done to give bots a wider range of opportunities to find an even better
+  *                 setup with weapons they would normally not chose in their current situation.
+  *                 And, on the other hand, a shocked bot does not really think but selects something random in panic.
+  *                 Reducers, Dirt weapons and weapons they do not have in stock are skipped.
+  *         2.3.3.3 Otherwise advance in the list of weapons. If no weapon was found (list exhausted), the first entry
+  *                 is preselected.
+  *         2.3.3.4 A shocked bot now ensures no item is selected, and if no weapon was available, the first one is
+  *                 chosen. Here the method ends and returns true.
+  *         2.3.3.5 Not shocked bots now rotate through their list of available items. If no items are left to try,
+  *                 item_curr is set to nullptr to indicate that no item selection is possible.
+  *         2.3.3.6 Unless the opponent has more than ten times the live points (including shields) than the bot,
+  *                 kamikaze selections are undone.
+  *         2.3.3.7 Unless the bot is buried or blocked, teleporter item and riot weapon selections are undone.
+  *         2.3.3.8 If both a weapon and an item was found, unselect the one with the lower score.
+  *         2.3.3.9 If neither weapon nor item was selected, but is_last is true, the small missile is selected.
+  *
+  *         Eventually return true if either weapon or item is selected and false if both are nullptr.
+  *
+  *       2.3.4 If all weapon selection attempts were used, but there are opponent selections left, weap_attempt is
+  *             reset to zero so another opponent can be selected.
+  *
+  *     2.4  If all weapon selection attempts for all opponent selection attempts were used up, breakUp is set to true
+  *          to indicate that all that could be done was tried.
+  *     2.5  If everything failed and this is the very last setup attempt, an emergency plan kicks in:
+  *          a) Try to teleport away.
+  *          b) If the bot has no teleporter in stock and the currently selected opponent is not buried, try to select
+  *             a swapper.
+  *          c) If no swapper was available or the currently selected opponent is buried, try the mass teleport.
+  *          d) If the bot has no mass teleport, select small missile.
+  *     2.6  If an item or weapon is chosen that shall be used to self destruct, check again whether this is really
+  *          wanted. If so, set mem_curr to the own entry, otherwise the selection either failed, or, if this is the
+  *          very last attempt and there was no successful setup, yet, revert to small missile.
+  *     2.7  If breakUp was set to true and this is not the last attempt, opp_attempt and weap_attempt are both reset
+  *          to zero to trigger a new full targetting cylcle.
+  *
+  *   3  done = calcAttack(int32_t attempt) - called if setupAttack() returned true.
+  *
+  *        Param 1 : Value of tgt_attempts
+  *
+  *      Here the basic attack values are calculated, trying to find an angle and a power value to begin with. These are
+  *      then checked and adapted according to the chosen item or weapon, the selected target, the situation of the bots
+  *      tank and whether the current level has a ceiling ("boxed mode") or not.
+  *
+  *      bool is_last   : (checked)       Set to true if attempt equals findTgtAttempts and no successful setup was
+  *                                       found, yet.
+  *      plStage        : (PS_CALCULATE)  AI enters the calculation stage.
+  *      isBlocked      : (false)         Will be set to true if the aiming finds out that a hill blocks the path.
+  *      needAim        : (false)         Will be set to true if a weapon is chosen that needs aiming.
+  *      curr_overshoot : (MAX_OVERSHOOT) The overshoot for the current aiming round.
+  *      offset_x       : (0)             Some weapons need a horizontal offset to aim at, like the napalm weapons.
+  *      offset_y       : (0)             Some weapons need a vertical offset to aim at, like the driller.
+  *
+  *     3.1  If an item is chosen, the currently set angle and power are written into curr_angle and curr_power. There
+  *          is no need for further investigation and aiming, so the method then returns true.
+  *     3.2  If the currently set opponent equals last_opp, and the currently set weapon is the same as what was used
+  *          against the last opponent, copy back the angle and power used in the last round and be done.
+  *          It is then tried to optimized the last attack.
+  *          This is only done if the same weapon is used, as other weapons might need different approaches.
+  *     3.3  If a laser is chosen and the tank is not buried enough to be evaluated as buried or blocked, return the
+  *          result of calcLaser(is_last is_last).
+  *
+  *            Param 1 : True if is_last in calcAttack() is true and needSuccess is true.
+  *
+  *          Set the angle to point directly at the selected opponent and see whether the opponent can be hit or not.
+  *
+  *          int32_t old_angle : (curr_angle) Backup the currently used angle to write it back if the aiming fails.
+  *          int32_t old_power : (curr_power) Backup the currently used power to write it back if the aiming fails.
+  *          double  drift     : (calculated) Variation of the angle to simulate that an exact aiming needs skill.
+  *
+  *       3.3.1 Set curr_power to the current tanks power, so no power change is necessary if this becomes the
+  *             attack that is performed.
+  *       3.3.2 Set curr_angle to an angle pointing directly at the selected target and add the value of 'drift'.
+  *       3.3.3 Follow the beam using a mind shot and calculate a hit score.
+  *       3.3.4 Use tank->shootClearance() to see whether the shot is blocked or crashes.
+  *             If the shot does not reach the target, and is_last is true, the hit_score (might have hit someone else)
+  *             is reduced. If this is not the very last attempt, curr_angle and curr_power are written back, needAim is
+  *             set to true and false is returned.
+  *
+  *     3.4  If the tank is buried, return the result of calcUnbury(bool is_last).
+  *
+  *            Param 1 : The value of is_last is simply transported.
+  *
+  *          Make sure that whatever is chosen is appropriate and points into the right direction.
+  *
+  *       3.4.1 If either a riot bomb is chosen, or a non-shaped weapon is selected while a self destruct attempt is
+  *             planned (*), the number of enemies on each side is counted to chose where to fire at.
+  *             After setting curr_angle and curr_power to appropriate values, they get sanitized, and angle is set to
+  *             curr_angle, power is set to curr_power, needAim is set to false as no further aiming is needed, and
+  *             isBlocked is set to true as this is the situation.
+  *             After that true is returned.
+  *       3.4.2 If a weapon is chosen and no self destruction is wanted, the shaped weapons are the only appropriate
+  *             weapons to free the trank without damaging itself. The current angle is set to 180°, the current power
+  *             is set to 10 plus some random variation according to the AI level. The current values then get
+  *             sanitized, and angle is set to curr_angle, power is set to curr_power, needAim is set to false as no
+  *             further aiming is needed, and isBlocked is set to true as this is the situation.
+  *             After that true is returned.
+  *       3.4.3 If this all fails but is_last is set to true, useFreeingTool() is called for an emergency selection.
+  *             As a last resort, the small missile is selected if useFreeingTool() did not succeed. The current angle
+  *             is set to a value between 100° and 160° degrees to either the left or right side, according to which
+  *             side is heavier buried. The current power is set to a value between 500 and 1,000. The current values
+  *             then get sanitized, and angle is set to curr_angle, power is set to curr_power, needAim is set to false
+  *             as no further aiming is needed, and isBlocked is set to true as this is the situation.
+  *             After that true is returned.
+  *       3.4.4 If everything failed, false is returned.
+  *
+  *     3.5  If the currently selected target is the bot itself, this is a self destruct attempt. Return the result of
+  *          calcKamikaze(bool is_last), then.
+  *
+  *            Param 1 : The value of is_last is simply transported.
+  *
+  *          As, at this point, a weapon is chosen, it must be checked whether it can be used and determined where to
+  *          fire it.
+  *
+  *       3.5.1 If a horizontal shaped weapon is selected, a free flat spot of the same height as where the tanks stands
+  *             on either the left or right side must be found.
+  *             If such a spot can be found, curr_angle and curr_power are set to values trying to hit it.
+  *       3.5.2 If a napalm weapon is chosen, curr_angle is set to 135° to the left or right side, whichever side has
+  *             head wind. curr_power is set to 100 plus an amount calculated using the current wind strength.
+  *       3.5.3 Otherwise fire the weapon upwards with more power the higher the spread value.
+  *       3.5.4 If this worked out, sanitize the current angle and power, write them into to angle and power, and return
+  *             true.
+  *       3.5.5 If this didn't work out but this is the last attempt, go through all valid self destruct items and
+  *             weapons until one is available or the small missile is selected. This is then just fired upwards and
+  *             true is returned.
+  *       3.5.6 In all other cases false is returned.
+  *
+  *     3.6  Otherwise this is normal aiming, and calcStandard(bool is_last, bool allow_flip_shot) is used to generated
+  *          the initial values to begin with.
+  *
+  *            Param 1 : The value of is_last is simply transported.
+  *            Param 2 : true, if the pre-incremented mem_curr->attempts is an even number, false otherwise.
+  *                      If set to true, the bot is allowed to shoot in the opposite direction. On steel walls, this
+  *                      parameter is ignored.
+  *
+  *          This method does no aiming but sets needed offsets and generates an angle and a power value to begin with.
+  *
+  *       3.6.1 calculate the offsets for the x and y coordinate needed by the chosen weapon. This is done with the
+  *             method calcOffset(bool is_last).
+  *
+  *               Param 1 : The value of is_last is simply transported.
+  *
+  *             This method calculate x and y offsets for weapons that need it. These offsets are stored in the AICore
+  *             members offset_x and offset_y, as they are needed in multiple places.
+  *
+  *             If the needed offset is off the screen, or makes no sense, the method returns false. But if is_last is
+  *             set to true, insane offsets are tried to be fixed. The idea is, that the bot tries nevertheless out of
+  *             pure desperation.
+  *
+  *         3.6.1.1 Napalm weapons need a horizontal offset where the wind blows the jellies over the opponent for
+  *                 greatest effect.
+  *         3.6.1.2 Shaped charges and their bigger versions need a horizontal offset that leads to a hit at either side
+  *                 of the opponent where the land height is equal enough to where the opponent stands, that the blast
+  *                 actually hits.
+  *         3.6.1.3 The driller must be placed above a buried tank, or under it if it is clinging to a steep hill side.
+  *
+  *       3.6.2 Calculate the starting angle, but limit it to:
+  *             - 20° to 35° if the opponent is below the bot,
+  *             - 40° to 55° if the opponent is at about the same height and
+  *             - 60° to 75° if the opponent is above the bot.
+  *             The angle is then modified according to the focusRate of the bot.
+  *       3.6.3 If a wrap wall is in place, check whether shooting through it results in a shorter shot, and flip the
+  *             angle if it is.
+  *       3.6.4 If allow_flip_shot is true and the wall is something else than a steel wall, bots may flip the shot with
+  *             a chance of 50% for a useless bot and 80% for a deadly bot.
+  *       3.6.5 Raise the angle until there is enough shooting clearance or the ceiling is hit.
+  *       3.6.6 If the angle becomes too steep, revert to the half way between the beginning angle and the currently
+  *             raised one. If this means an obstacle is in the way, the bot might decide to remove it first.
+  *       3.6.7 Calculate starting power as a raw estimation using the simple distance.
+  *       3.6.8 If the shot is already known to be blocked, write back the current angle and power to the used angle
+  *             and power and set needAim to false.
+  *
+  *     3.7  In boxed mode, the situation regarding the ceiling must be checked, but only if calcStandard() succeeded,
+  *          the tank is not blocked and a weapon is chosen that needs aiming.
+  *          This is done by calling calcBoxed(bool is_last).
+  *
+  *            Param 1 : The value of is_last is simply transported.
+  *
+  *       3.7.1 If is_last is false, the bot might "forget" to check for ceiling hits. The chance is between 33% for the
+  *             useless bot and 7% for the deadly+1 bot.
+  *       3.7.2 As long as the shot is regarded to be crashed, but the tracing was finished (not too many bounces/wraps)
+  *             and either angle or power can be modified, traceShot() is used to see where the shot would end with the
+  *             current angle and power.
+  *
+  *         3.7.2.1 If the shot ends in a steel wall or ceiling, or if the shot hits the floor bottom through a wrap
+  *                 ceiling but is not a digging weapon, the shot is regarded to be crashed.
+  *         3.7.2.2 If the shot crashed, either the current angle, power or both are modified by one step. The angle is
+  *                 changed to flatten the shot and the power is reduced.
+  *         3.7.2.3 If the angle reaches 90°/270°, it can no longer be modified.
+  *         3.7.2.4 If the power reaches MIN_POWER, it can no longer be modified.
+  *
+  *       3.7.3 If the shot has not crashed and the tracing was finished but the shot did not reach its target, the path
+  *             is considered to be blocked. If is_last is true and the map has a steel or wrap wall and there was no
+  *             positive setup score already, the path is tried to be cleared.
+  *
+  *         3.7.3.1 Decide whether to free the tank or to remove an obstacle.
+  *         3.7.3.2 Use calcUnbury() if the tank is to be freed.
+  *         3.7.3.3 Flatten the current angle if an obstacle is to be removed.
+  *         3.7.3.4 Set needAim to false and isBlocked to true.
+  *         3.7.3.5 Directly return true.
+  *
+  *       3.7.4 If no emergency freeing is possible, is_last is false, or its the wrong wall type or a positive setup
+  *             has already been found, directly return false.
+  *       3.7.5 In all other cases return true if the last shot did not crash or if is_last is true, and false
+  *             otherwise.
+  *
+  *     3.8  Eventually return the value of 'result'.
+  *
+  *   4  done = calcAim(bool is_last) - called if calcAttack() returned true, needAim is true and isBlocked is false.
+  *
+  *        Param 1 : True if tgt_attempts equals findTgtAttempts and needSuccess is still true.
+  *
+  *      Here the aiming is done for the selected weapon against the selected target.
+  *
+  *      plStage        : (PS_AIM)              The AI enters the aiming stage.
+  *      hill_detected  : (false)               Some situations indicate that a hill is between the tank and its target.
+  *      best_score     : (NEUTRAL_ROUND_SCORE) Used to record the best setup for the current aiming round.
+  *      best_angle     : (angle)               Used to record the angle that achieved the best aiming round score.
+  *      best_power     : (power)               Used to record the power that achieved the best aiming round score.
+  *      best_prime_hit : (false)               Used to record whether the best aiming round settings hit the primary
+  *                                             target.
+  *      best_overshoot : (MAX_OVERSHOO)        Used to record the overshoot the best setup had.
+  *      last_ang_mod   : (0)                   Note down the used angle modifications, so the next aiming attempt knows
+  *                                             the last modification to the angle.
+  *      last_pow_mod   : (0)                   Note down the used power modifications, so the next aiming attempt knows
+  *                                             the last modification to the power.
+  *      last_overshoot : (MAX_OVERSHOOT)       Note down the achieved overshoot, so the next aiming attempt knows
+  *                                             whether the shot gets nearer to the target or not.
+  *      last_reverted  : (false)               Set to true if an aiming attempt reverts the previous modifications.
+  *      last_score     : (0)                   Note down the achieved score, so the next aiming attempt knows whether
+  *                                             the new hit is really better or not.
+  *      last_was_better: (false)               Set to true if the previous score was better than the current one.
+  *      reached_x      : (x)                   Used as a short cut and memory of where the current attempt hits.
+  *      reached_y      : (y)                   Used as a short cut and memory of where the current attempt hits.
+  *
+  *      The aiming is done as long as the number of aiming attempts have not reached findRngAttempts.
+  *
+  *     4.1  Generate an angle modifier in the interval [1;7], with 2 being the maximum for the useless and 7 the
+  *          maximum for the deadly+1 AI, and a power modifier in the interval [10;340] with 220 being the maximum for
+  *          useless and 340 being the maximum for deadly bots.
+  *
+  *     4.2  Use void traceWeapon(int32_t &has_crashed, int32_t &has_finished)
+  *          to see where the used weapon using curr_angle and curr_power will end.
+  *
+  *            Param 1: has_crashed is set to the number of projectiles that crashed into a steel wall or ceiling.
+  *                     If the level has a wrap wall ceiling and the projectile can not dig and thus would explode on
+  *                     the very bottom of the screen due to dirt being in the way, it is considered to have crashed,
+  *                     too.
+  *            Param 2: has_finished is set to the number of projectiles that have been traced to the end. The bots can
+  *                     only trace maxBounce wall and ceiling bounces or wraps. If the number of actual bounces exceeds
+  *                     this limit, the projectile is considered unfinished and the tracing stops.
+  *
+  *          Basically this method uses traceShot() for each spread projectile of the weapon. Non-spread weapons have a
+  *          spread value of 1, so this can be done for every weapon.
+  *          Weapons with submunition are then traced further using traceCluster(), which will, like traceWeapon() does
+  *          on weapons without submunition, use calcHitDamage() to generate the damage values on each tank.
+  *
+  *          The nearest hit to the primary target is recorded in curr_overshoot, reached_x and reached_y.
+  *
+  *     4.3  Use int32_t calcHitScore(bool is_last) to generate a score out of all damage dealt.
+  *
+  *            Param 1: is_last is set to true if is_last in aim() is true and needSuccess is true.
+  *
+  *          This method cycles through the opponents memory, and sums up the damage done with curr_weap to a total
+  *          score according to a) how much damage over the opponents health (aka overkill) has been done and b) on
+  *          which team they are compared to us.
+  *
+  *          If the primary target was not hit, the score is ensured to be negative, as collateral damage is
+  *          discouraged. However, this is only done if is_last is false, as collateral damage with a total positive
+  *          score is better than nothing on the very last attempt.
+  *
+  *     4.4  If a new best_score is achieved, and either the primary target was hit, or hasn't been hit before, the
+  *          best_* values are set to the current ones:
+  *
+  *            best_angle     = curr_angle
+  *            best_overshoot = curr_overshoot
+  *            best_power     = curr_power
+  *            best_prime_hit = curr_prime_hit
+  *            best_score     = hit_score
+  *
+  *     4.5  Basically there are four different situations that need different actions.
+  *          If the shot, or some of the spread or cluster shots, did not finish, it must be tried to change the used
+  *          angle and power so the next attempt does finish.
+  *          With steel walls or a wrapped ceiling ceiling with dirt on the ground the shot can have crashed. This can
+  *          be fixed by getting away from 45° and reducing power.
+  *          The hit might be nearer than the last one. In this case the last modifications seem to have moved the angle
+  *          and power into the right direction, which is a path to follow further.
+  *          And finally the hit might be farther away. The last modifications might have been too strong or in the
+  *          wrong direction.
+  *
+  *       4.5.1 Try to fix unfinished shots using void fixUnfinished(int32_t &ang_mod, int32_t &pow_mod).
+  *
+  *               Param 1 : Reference to the angle modifier to adapt.
+  *               Param 2 : Reference to the power modifier to adapt.
+  *
+  *             If the angle is too steep, lower it, if it is too flat, raise it. The angle is, however, limited
+  *             according to where the opponent is. If it is above the own tank, the angle is limited around 60°. If
+  *             it is at an equal height in the range of +/- 100 pixels, the angle is limited around 40°. If it is
+  *             below, the angle is limited around 20°.
+  *
+  *             But if hill_detected is true, the angle modification is limited to 1° per aiming round.
+  *
+  *             The power is reduced if it is greater than twice the x distance to the opponen, and raised if it is less
+  *             then the x distance. If it is between the two, power is not changed.
+  *
+  *             last_reverted   : true if last_ang_mod is signed differently than the resulting ang_mod.
+  *             last_was_better : false
+  *
+  *       4.5.2 Try to fix crashed shots using void fixCrashed(int32_t &ang_mod, int32_t &pow_mod).
+  *
+  *               Param 1 : Reference to the angle modifier to adapt.
+  *               Param 2 : Reference to the power modifier to adapt.
+  *
+  *             Here the angle modifier is limited to 1° per aiming round if hill_detected is false. this is done,
+  *             because if not trying to get over hill the crashes normally occur if the shot is just a bit too strong.
+  *
+  *             If the angle is above 60° in boxed mode, it is reduced by a random amount between [1;7]° according
+  *             to ai_level. In non-boxed mode it is reduced by 1° if it is between 10° and 45°. Otherwise the angle is
+  *             left alone.
+  *
+  *             If the power is greater than the simple x distance, it is reduced as well, even more if it is 50% and
+  *             more greater than the x distance.
+  *
+  *             Now if the hit_score is positive, halve it for every shot that crashed. The hit_score has of course
+  *             been written into best_score already if it was better, but the modified version will be used for
+  *             last_score later.
+  *
+  *             last_reverted   : true if last_ang_mod is signed differently than the resulting ang_mod.
+  *             last_was_better : false
+  *
+  *       4.5.3 If the shot did finish and did not crash and hit nearer to the target than the last, a few adaptations
+  *             might be needed.
+  *
+  *             If the resulting hit_score is worse than last_score, it must be ensured, that ang_mod has the same sign
+  *             as the previous one. The direction is correct, as the hit is nearer, but it hasn't been changed enough.
+  *
+  *             To get even nearer, pow_mod is ensured to have the opposite sign of curr_overshoot. So if curr_overshoot
+  *             is negative, the shot is still too short and pw_mod must be positive and vice versa.
+  *
+  *             last_reverted   : true if last_ang_mod is signed differently than the resulting ang_mod.
+  *             last_was_better : true if hit_score is lower than last_score, false otherwise.
+  *
+  *       4.5.4 If the shot did finish and did not crash but hit farther away than the last, use
+  *             void fixOvershoot(int32_t& ang_mod, int32_t& pow_mod, int32_t hit_score).
+  *
+  *               Param 1 : Reference to the angle modifier to adapt.
+  *               Param 2 : Reference to the power modifier to adapt.
+  *               Param 3 : The hit_score achieved with the shot. This is local to AICore::aim() and must be submitted.
+  *
+  *             bool angle_was_optimized : true  if the last modification brought the angle nearer to 45° on its side,
+  *                                              and false otherwise.
+  *             hill_detected            : false if both the overshoot and the hit_score are positive. Otherwise the
+  *                                              current value is not changed.
+  *
+  *             Here are some more possible (sub) situations to consider:
+  *             1) The current score is at least better than the last.
+  *                This can happen if the shot does no longer hit team mates.
+  *                The important situation is, if the overshoot is very small and a new best score is achieved.
+  *                The bigger the weapon, the higher the probability that this might be the case.
+  *
+  *                If the hit_score is lower than best_score, meaning no new best score was achieved, it is ensured
+  *                that both ang_mod and pow_mod have the same sign than the last modifications had. Without a new
+  *                best_score the higher one might only mean less collateral damage, and no adaptation is done.
+  *
+  *                last_was_better : true
+  *                hill_detected   : false
+  *
+  *             2) Both the current and the last overshoot were negative, the angle was optimized towards 45° and the
+  *                power was raised.
+  *                Having a worse overshoot then can happen if the gun was lowered and the shot crashes into the side of
+  *                a hill or mountain.
+  *                The angle must then be brought towards 180° more than the last angle modification brought it away
+  *                from it.
+  *
+  *                last_was_better : false, no matter what, so this change won't get directly reverted again.
+  *                hill_detected   : true;
+  *
+  *             3) The current score is worse than the last score.
+  *                 a) The last score was better than the one before.
+  *                    The modifications might have been too strong, try values between the two.
+  *                 b) That was two worse tries in a row.
+  *                    The direction was wrong, and the last modifications must be reverted and strengthened by the
+  *                    current set modifications.
+  *
+  *                last_was_better : false
+  *
+  *             4) No last score or the same.
+  *                Just adapt the mods according to whether the shot was too short or too long.
+  *
+  *                last_was_better : false
+  *
+  *             last_reverted   : true if last_ang_mod is signed differently than the resulting ang_mod.
+  *
+  *     4.6  If the current angle is 180°, so pointing straight up, ang_mod is zero and no hit_score greater than zero
+  *          was achieved, a random ang_mod in the interval [2;6] towards the opponent is generated to fix this vertical
+  *          shot. Vertical trick shots using wind are only accepted if the resulting hit_score is positive.
+  *
+  *     4.7  If the power modification according to the current overshoot and ang_mod is too low, it is strengthened
+  *          using the difference of the overshoot and pow_mod divided by ang_mod and multiplied by the AI's focusRate.
+  *
+  *     4.8  Sanitize ang_mod and pow_mod, both applied must not lead to invalid values. Then apply both to curr_angle
+  *          and curr_power.
+  *
+  *     4.9  Save the current values in the last_* members:
+  *
+  *            last_ang_mod   = ang_mod
+  *            last_overshoot = curr_overshoot
+  *            last_pow_mod   = pow_mod
+  *            last_score     = hit_score
+  *
+  *          After this, the loop ends and the work flow restarts at 4.1.
+  *
+  *     4.10 When all aiming attempts are used up, an emergency plan to free the tank or unblock its path might be
+  *          triggered if all of the following conditions are true:
+  *
+  *          - is_last and needSuccess are both true,
+  *          - there was no best setup with a positive score, yet,
+  *          - the current best round score will not create a new best setup with a positive score,
+  *          - the overall best score is negative,
+  *          - the best achieved overshoot is negative, indicating that the target was not yet reached and
+  *          - either the overshoot is greater than the weapons radius without being a ceiling crash, or fixOvershoot()
+  *            detected a hill in the path.
+  *
+  *     4.11 Eventually, if a new best_round_score is achieved, remember the current settings:
+  *
+  *            best_round_score = best_score;
+  *            curr_angle       = best_angle;
+  *            curr_power       = best_power;
+  *
+  *     4.12 Return true if either best_round_score is larger than zero, or both is_last and needSuccess are true.
+  *
+  *   5  If the aiming was successful, a few more checks are made.
+  *
+  *     5.1  If best_round_score is greater than zero, and either more weapons have been tried than the ai_level is or
+  *          all findOppAttempts have been used up, both the weap_attempts and opp_attempts are reset to zero so this
+  *          targeting attempt is declared to be over.
+  *
+  *     5.2  If the primary target was hit, its score is added to best_round_score. The opponents score is divided by
+  *          ten times the ai_level for this unless it is the revengee, in which case the opponent score is only divided
+  *          by the simple ai_level. This is done to enhance scores for attacks where the primary target was hit over
+  *          attempts, were only collateral damage was done, but counted positive due to last attempt behaviour.
+  *          Further this makes shots against revengees more likely, even if they are imperfect, the lower the AI level
+  *          is.
+  *
+  *     5.3  If a weapon with a single damage value over zero, so no dirt bomb or reducer, was chosen, its score divided
+  *          by ten times the ai_level is added, too.
+  *
+  *     5.4  If a new best setup score was achieved, or the primary target was first hit, or a success must be enforced,
+  *          the best setup data is remembered:
+  *
+  *          best_setup_angle     = curr_angle
+  *          best_setup_item      = item_curr
+  *          best_setup_mem       = mem_curr
+  *          best_setup_overshoot = best_overshoot
+  *          best_setup_power     = curr_power
+  *          best_setup_prime     = best_prime_hit
+  *          best_setup_weap      = weap_curr
+  *          best_setup_score     = best_round_score
+  *
+  *          As this targeting round is then definitely over, opp_attempts and weap_attempts are reset to zero to
+  *          trigger a fresh new round.
+  *
+  *      The main targeting loop ends here.
+  *
+  *   6  If a new revengee was set, or the old removed, save this in player->revenge.
+  *
+  *   7  Without a real setup with a positive score here, a last freeing attempt might be triggered. But only if the
+  *      AI did not detect that it was blocked already, or a freeing attempt would have been setup already.
+  *
+  *   8  Write back the best attack setup for the tank to use.
+  *
+  *   9  Shout a retaliation phrase out if the revengee is attacked and the predicted damage is at least 50% of the
+  *      opponents total health.
+  *
+  *  10  Otherwise, if a self destruct attempt is issued, shout out a good-bye-phrase.
+  *
+  *  11  If a different target than in the last round is attacked, add some "last-second-errors".
+**/
+class AICore
+{
+public:
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit AICore();
+	~AICore();
+
+	// No copying, no assignment
+	AICore(const AICore&) = delete;
+	AICore &operator=(const AICore&) = delete;
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	PLAYER* active_player() const;
+	void    allowText    ();
+	bool    can_work     () const;
+	void    forbidText   ();
+	bool    hasExited    () const;
+	bool    start        (PLAYER* player_);
+	bool    status       (int32_t &aItem, int32_t &aAngle,
+	                      int32_t &aPower, ePlayerStages &pl_stage);
+	void    stop         ();
+	void    weapon_fired ();
+
+	void    operator()   ();
+
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+private:
+
+	typedef ePlayerStages             plStage_t;
+	typedef sItemListEntry            itEntry_t;
+	typedef sOppMemEntry              opEntry_t;
+	typedef sWeapListEntry            weEntry_t;
+	typedef std::mutex                mutex_t;
+	typedef std::condition_variable   condv_t;
+	typedef std::lock_guard<mutex_t>  lguard_t;
+	typedef std::unique_lock<mutex_t> luniq_t;
+
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	bool        aim            (bool is_last);
+	bool        calcAttack     (int32_t attempt);
+	bool        calcBoxed      (bool is_last);
+	void        calcHitDamage  (int32_t hit_x, int32_t hit_y, double weap_rad,
+	                            double dmg, weaponType weapType);
+	int32_t     calcHitScore   (bool is_last);
+	bool        calcKamikaze   (bool is_last);
+	bool        calcLaser      (bool is_last);
+	bool        calcOffset     (bool is_last);
+	bool        calcStandard   (bool is_last, bool allow_flip_shot);
+	bool        calcUnbury     (bool is_last);
+	void        checkItemMem   ();
+	void        checkOppMem    ();
+	void        checkWeapMem   ();
+	void        destroy        ();
+	void        flattenCurrAng ();
+	void        fixCrashed     (int32_t &ang_mod, int32_t &pow_mod);
+	void        fixOvershoot   (int32_t &ang_mod, int32_t &pow_mod,
+	                            int32_t hit_score);
+	void        fixUnfinished  (int32_t &ang_mod, int32_t &pow_mod);
+	const char* getLevelName   (int32_t level) const;
+	bool        getMemory      ();
+	bool        initialize     ();
+	void        sanitizeCurr   ();
+	bool        selectItem     (bool is_last);
+	bool        selectTarget   (bool is_last);
+	bool        setupAttack    (bool is_last, int32_t &opp_attempt,
+	                            int32_t &weap_attempt);
+	void        showFeedback   (const char* const feedback, int32_t col,
+	                            double yv, eTextSway text_sway, int32_t dur);
+	void        traceCluster   (int32_t subType, int32_t subCount,
+	                            int32_t sub_x, int32_t sub_y,
+	                            double inh_xv, double inh_yv);
+	bool        traceShot      (int32_t trace_angle,
+	                            bool &finished, bool &top_wrapped,
+	                            int32_t &reached_x_, int32_t &reached_y_,
+	                            double &end_xv, double &end_yv);
+	void        traceWeapon    (int32_t &has_crashed, int32_t &has_finished);
+	void        updateItemScore(itEntry_t* pItem);
+	void        updateOppScore (opEntry_t* pOpp);
+	void        updateWeapScore(weEntry_t* pWeap);
+	bool        useFreeingTool (bool free_tank, bool is_last);
+	bool        useItem        (itemType item_type);
+	bool        useItem        (int32_t item_index);
+	bool        useWeapon      (weaponType weap_type);
+	bool        useWeapon      (int32_t weap_index);
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	// Internal values
+	mutex_t    actionMutex;
+	condv_t    actionCondition;
+	volatile
+	bool       canWork          = true;
+	int32_t    curr_angle       = 90;    //!< The angle that is currently tested
+	int32_t    curr_overshoot   = 0;     //!< Current calculated distance of hit versus opponent
+	int32_t    curr_power       = 0;     //!< The power that is currently tested
+	bool       curr_prime_hit   = false; //!< Whether the primary target was hit.
+	double     errorMultiplier  = 0.;    //!< Default error reduction according to AI level
+	int32_t    findOppAttempts  = 0;     //!< Number of attempts to select a suitable opponent
+	int32_t    findRngAttempts  = 0;     //!< Number of attempts to aim the current selection
+	int32_t    findTgtAttempts  = 0;     //!< Number of attempts to come up with an attack plan
+	int32_t    findWeapAttempts = 0;     //!< Number of attempts to find a suitable item/weapon
+	double     focusRate        = 0.;    //!< How good a bot can focus on a specific task
+	bool       isBlocked        = false; //!< Set to true if a shot can't get through
+	volatile
+	bool       isFinished       = false; //!< Set to true when operator() ends
+	bool       isShocked        = false;
+	volatile
+	bool       isStopped        = false;
+	volatile
+	bool       isWorking        = false;
+	int32_t    maxBounce        = 0;     //!< How many wall bounces/wraps can be calculated
+	bool       needAim          = false; //!< true if this is a standard shot
+	bool       needSuccess      = true;  //!< true unless a best score is achieved
+	int32_t    offset_x         = 0;
+	int32_t    offset_y         = 0;
+	plStage_t  plStage          = PS_AI_IS_IDLE;
+	sOpponent* revengee         = nullptr; //!< If set, it is tried first as a target
+	sOpponent* shocker          = nullptr; //!< The current fear shock winner
+	abool_t    textAllowed;                //!< Is new FLOATTEXT allowed?
+	int32_t    weap_idx         = SML_MIS;
+
+	// Values taken from the player and their tank
+	int32_t    ai_level    = 0;       //!< To not having to cast from player type.
+	double     ai_level_d  = 0.;      //!< To not having to cast from ai_level.
+	double     ai_over_mod = 0.;      //!< modifier for overkills and similar
+	double     ai_type_mod = 0.;      //!< modifier for important decisions
+	int32_t    angle       = 90;      //!< The currently determined best angle
+	double     blast_min   = 0.;      //!< Damage done by small missile
+	double     blast_med   = 0.;      //!< Damage done by medium or large missile
+	double     blast_big   = 0.;      //!< Damage done by small nuke or nuke
+	double     blast_max   = 0.;      //!< Damage done by death head
+	int32_t    buried      = 0;       //!< Full buried level
+	int32_t    buried_l    = 0;       //!< left side buried level
+	int32_t    buried_r    = 0;       //!< right side buried level
+	double     currLife    = 0.;
+	itEntry_t* item_curr   = nullptr; //!< Currently selected entry
+	itEntry_t* item_head   = nullptr; //!< Last selected entry
+	itEntry_t* item_last   = nullptr; //!< Entry with highest score
+	int32_t    last_ang    = 0;       //!< Angle used in last round
+	sOpponent* last_opp    = nullptr; //!< The opponent attacked in the last round
+	int32_t    last_pow    = 0;       //!< Power used in last round
+	int32_t    last_weap   = 0;       //!< weapon used in the last round
+	int32_t    maxLife     = 100;
+	opEntry_t* mem_curr    = nullptr; //!< Currently selected entry
+	opEntry_t* mem_head    = nullptr; //!< Last selected entry
+	opEntry_t* mem_last    = nullptr; //!< Entry with highest score
+	bool       needMoney   = false;   //!< Might alter some decisions
+	PLAYER*    player      = nullptr;
+	int32_t    power       = 0;       //!< The currently determined best power
+	TANK*      tank        = nullptr;
+	double     type_mod    = 1.;
+	weEntry_t* weap_curr   = nullptr; //!< Currently selected entry
+	weEntry_t* weap_head   = nullptr; //!< Last selected entry
+	weEntry_t* weap_last   = nullptr; //!< Entry with highest score
+	double     x           = 0.;
+	double     y           = 0.;
+
+	// Values to remember settings and situations over aiming rounds
+	int32_t    best_angle      = 0;
+	int32_t    best_overshoot  = MAX_OVERSHOOT; //!< Overshoot value of currently best angle and power
+	int32_t    best_power      = 0;
+	bool       best_prime_hit  = false; //!< Whether the bes aiming round values hit the primary target.
+	int32_t    best_score      = NEUTRAL_ROUND_SCORE;
+	bool       hill_detected   = false;
+	int32_t    last_ang_mod    = 0;
+	int32_t    last_overshoot  = MAX_OVERSHOOT;
+	int32_t    last_pow_mod    = 0;
+	bool       last_reverted   = false;
+	int32_t    last_score      = 0;
+	bool       last_was_better = false;
+	int32_t    reached_x       = x;
+	int32_t    reached_y       = y;
+
+	// Values used to memorize the best setup in a round
+	int32_t    best_round_score     = NEUTRAL_ROUND_SCORE;
+	int32_t    best_setup_angle     = 0;
+	itEntry_t* best_setup_item      = nullptr;
+	opEntry_t* best_setup_mem       = nullptr;
+	int32_t    best_setup_overshoot = MAX_OVERSHOOT;
+	int32_t    best_setup_power     = 0;
+	bool       best_setup_prime     = false;
+	int32_t    best_setup_score     = 0;
+	weEntry_t* best_setup_weap      = nullptr;
+};
+
+#endif // ATANKS_SRC_AICORE_H_INCLUDED
+
diff --git a/src/allegro.h b/src/allegro.h
deleted file mode 100644
index d5e7fc3..0000000
--- a/src/allegro.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*         ______   ___    ___
- *        /\  _  \ /\_ \  /\_ \
- *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
- *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
- *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
- *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
- *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
- *                                           /\____/
- *                                           \_/__/
- *
- *      Main header file for the entire Allegro library.
- *      (separate modules can be included from the allegro/ directory)
- *
- *      By Shawn Hargreaves.
- *
- *      Vincent Penquerc'h split the original allegro.h into separate headers.
- *
- *      See readme.txt for copyright information.
- */
-
-
-#ifndef ALLEGRO_H
-#define ALLEGRO_H
-
-#include "allegro/base.h"
-
-#include "allegro/system.h"
-#include "allegro/debug.h"
-
-#include "allegro/unicode.h"
-
-#include "allegro/mouse.h"
-#include "allegro/timer.h"
-#include "allegro/keyboard.h"
-#include "allegro/joystick.h"
-
-#include "allegro/palette.h"
-#include "allegro/gfx.h"
-#include "allegro/color.h"
-#include "allegro/draw.h"
-#include "allegro/rle.h"
-#include "allegro/compiled.h"
-#include "allegro/text.h"
-#include "allegro/font.h"
-
-#include "allegro/fli.h"
-#include "allegro/config.h"
-#include "allegro/gui.h"
-
-#include "allegro/sound.h"
-
-#include "allegro/file.h"
-#include "allegro/lzss.h"
-#include "allegro/datafile.h"
-
-#include "allegro/fixed.h"
-#include "allegro/fmaths.h"
-#include "allegro/matrix.h"
-#include "allegro/quat.h"
-
-#include "allegro/3d.h"
-#include "allegro/3dmaths.h"
-
-
-#ifndef ALLEGRO_NO_COMPATIBILITY
-   #include "allegro/alcompat.h"
-#endif
-
-#ifndef ALLEGRO_NO_FIX_CLASS
-   #ifdef __cplusplus
-      #include "allegro/fix.h"
-   #endif
-#endif
-
-
-#ifdef ALLEGRO_EXTRA_HEADER
-   #include ALLEGRO_EXTRA_HEADER
-#endif
-
-#endif          /* ifndef ALLEGRO_H */
-
-
diff --git a/src/atanks.cpp b/src/atanks.cpp
index c434666..1d535eb 100644
--- a/src/atanks.cpp
+++ b/src/atanks.cpp
@@ -1,3 +1,5 @@
+#define ATANKS_SRC_ATANKS_CPP 1
+
 /*
  * atanks - obliterate each other with oversize weapons
  * Copyright (C) 2002,2003  Thomas Hudson,Juraj Michalek
@@ -17,46 +19,27 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  * */
 
-
-#include <dirent.h>
-
+#include "debug.h"
 #include "globals.h"
-#include "menu.h"
+#include "optionscreens.h"
+#include "player.h"
 #include "button.h"
-#include "team.h"
 #include "files.h"
-#include "satellite.h"
 #include "update.h"
-#include "network.h"
-#include "land.h"
-
-#include "floattext.h"
-#include "explosion.h"
+#include "tank.h"
 #include "beam.h"
 #include "missile.h"
-#include "decor.h"
-#include "teleport.h"
-#include "sky.h"
 #include "gameloop.h"
+#include "clock.h"
 
-#ifdef THREADS
-#include <pthread.h>
-#endif
 
 #ifdef NETWORK
-#include "client.h"
+# include <thread>
+# include "client.h"
 #endif
 
 
-enum cmdTokens
-{
-  ARGV_NOTHING_EXPECTED,
-  ARGV_GFX_DEPTH,
-  ARGV_SCREEN_WIDTH,
-  ARGV_SCREEN_HEIGHT,
-  ARGV_DATA_DIR,
-  ARGV_CONFIG_DIR
-};
+#define HELP_REQUESTED -100
 #define SWITCH_HELP "-h"
 #define SWITCH_FULL_SCREEN "-fs"
 #define SWITCH_WINDOWED "--windowed"
@@ -66,4969 +49,1353 @@ enum cmdTokens
 #define SWITCH_NO_CONFIG "--noconfig"
 
 
-int screen_mode = GFX_AUTODETECT_WINDOWED;
-
-// BITMAP *create_gradient_strip (const gradient *gradient, int length);
-// int draw_circlesBG (GLOBALDATA *global, BITMAP *dest, int x, int y, int width, int height, bool image);
-
-using namespace std;
-
-void fpsadd()
-{
-  fps = frames;
-  frames = 0;
-}
-
-#ifdef THREADS
-pthread_mutex_t* cclock_lock;
-#endif
-void destroy_cclock_lock() {
-  #ifdef THREADS
-  if (cclock_lock)
-  {
-    int result = pthread_mutex_destroy(cclock_lock);
-    switch (result)
-    {
-      case 0:
-        //Successfully destroyed
-        break;
-      case EBUSY:
-        //Some thread forgot to unlock result
-        printf("%s:%i: Lock is still held.\n", __FILE__, __LINE__);
-        break;
-      case EINVAL:
-        //Invalid lock
-        printf("%s:%i: Lock is invalid.\n", __FILE__, __LINE__);
-        break;
-      default:
-        printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_destroy.\n", __FILE__, __LINE__, result);
-        break;
-    }
-    free(cclock_lock);
-  }
-  #endif
-}
-void init_cclock_lock()
-{
-  #ifdef THREADS
-  cclock_lock = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
-  if (cclock_lock == NULL)
-  {
-    printf("%s:%i: Could not allocate memory.\n", __FILE__, __LINE__);
-  }
-  int result = pthread_mutex_init(cclock_lock, NULL);
-  switch (result)
-  {
-    case 0:
-      //Succesfully initialized
-      break;
-    case EAGAIN:
-      //Not enough resources
-      printf("%s:%i: Not enough resources to create mutex.\n", __FILE__, __LINE__);
-      break;
-    case ENOMEM:
-      printf("%s:%i: Not enough memory to create mutex.\n", __FILE__, __LINE__);
-      break;
-    case EPERM:
-      printf("%s:%i: Not authorized.\n", __FILE__, __LINE__);
-      break;
-    case EBUSY:
-      printf("%s:%i: The mutex is already initialized.\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      printf("%s:%i: Invalid attribute.\n", __FILE__, __LINE__);
-      break;
-    default:
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_init.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  atexit(destroy_cclock_lock);
-  #endif
-}
-void lock_cclock()
-{
-  #ifdef THREADS
-  int result = pthread_mutex_lock(cclock_lock);
-  switch (result)
-  {
-    case 0:
-      //Got the lock.
-      break;
-    case EINVAL:
-      //Priority too high
-      printf("%s:%i: Either this thread's priority is higher than the mutex's priority, or the mutex is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    case EAGAIN:
-      printf("%s:%i: Too many locks on the mutex.\n", __FILE__, __LINE__);
-      break;
-    case EDEADLK:
-      //We have the lock already
-      printf("%s:%i: Already have lock.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //What error is this?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_lock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
-}
-void unlock_cclock()
-{
-  #ifdef THREADS
-  int result = pthread_mutex_unlock(cclock_lock);
-  switch (result)
-  {
-    case 0:
-      //Released the lock
-      break;
-    case EPERM:
-      //Forgot to get a lock on the mutex
-      printf("%s:%i: Mutex isn't locked\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      //Uninitialized mutex
-      printf("%s:%i: cclock_lock is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_unlock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
-}
-int get_cclock()
-{
-  lock_cclock();
-  int c = cclock;
-  unlock_cclock();
-  return c;
-}
-void clockadd()
-{
-  lock_cclock();
-  cclock++;
-  unlock_cclock();
-}
-END_OF_FUNCTION(clockadd)
-
-
-
-
-/*****************************************************************************
-drawMenuBackground
-
-Draws a 600x400 centered box, fills it with some random lines or circles.
-Someday, we should make this more generic; have it take the box dimensions
-as an input parameter.
-*****************************************************************************/
-void drawMenuBackground (GLOBALDATA *global, ENVIRONMENT *env, int itemType, int tOffset, int numItems)
-{
-  rectfill (env->db, global->halfWidth - 300, global->menuBeginY, // 100,
-            global->halfWidth + 300, global->menuEndY, // global->screenHeight - 100,
-            makecol (0,79,0));
-  rect     (env->db, global->halfWidth - 300, global->menuBeginY, // 100,
-            global->halfWidth + 300, global->menuEndY, // global->screenHeight - 100,
-            makecol (128,255,128));
-
-  drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-  env->current_drawing_mode = DRAW_MODE_TRANS;
-  set_trans_blender (0, 0, 0, 15);
-  for (int tCount = 0; tCount < numItems; tCount++)
-    {
-      int radius, xpos, ypos;
-      switch (itemType)
-        {
-        case BACKGROUND_CIRCLE: // circles
-          radius = (int)((perlin1DPoint (1.0, 5, (tOffset * 0.02) + tCount + 423346, 0.5, 8) + 1) / 2 * 40);
-          xpos = global->halfWidth + (int)(perlin1DPoint (1.0, 3, (tOffset * 0.02) + tCount + 232662, 0.3, 6) * 250);
-          ypos = global->halfHeight + (int)(perlin1DPoint (1.0, 2, (tOffset * 0.02) + tCount + 42397, 0.3, 6) * (global->halfHeight - 100));
-          circlefill (env->db, xpos, ypos, radius,
-                      makecol (200,255,200));
-          break;
-        case BACKGROUND_LINE: // Horz lines
-          radius = (int)((perlin1DPoint (1.0, 5, (tOffset * 0.02) + tCount + 423346, 0.5, 8) + 1) / 2 * 40);
-          xpos = global->halfWidth + (int)(perlin1DPoint (1.0, 3, (tOffset * 0.02) + tCount + 232662, 0.3, 6) * 250);
-          rectfill (env->db, xpos - radius / 2, 101,
-                    xpos + radius / 2, global->screenHeight - 101,
-                    makecol (200,255,200));
-          break;
-        case BACKGROUND_BLANK:
-        default:
-          break;
-        }
-
-    }
-  solid_mode ();
-  env->current_drawing_mode = DRAW_MODE_SOLID;
-}
-
-
-void initialisePlayers (GLOBALDATA *global)
-{
-  int z;
-
-  for (z = 0; z < global->numPlayers; z++)
-    {
-      global->players[z]->money = (int)global->startmoney;
-      global->players[z]->score = 0;
-      // global->players[z]->initialise ();
-      // global->players[z]->type_saved = global->players[z]->type;
-      if (((int)global->players[z]->type != HUMAN_PLAYER) &&
-          (global->players[z]->preftype == PERPLAY_PREF))
-        {
-          global->players[z]->generatePreferences();
-        }
-      global->players[z]->initialise();
-      global->players[z]->type_saved = global->players[z]->type;
-    }
-}
-
-
-
-void wait_for_input()
-{
-  do
-    {
-      LINUX_SLEEP;
-    }
-  while ((!keypressed()) && (!mouse_b));
-
-  flush_inputs();
-
-}
-
-int pickColor (int left, int top, int width, int height, int x, int y)
-{
-  int r, g, b;
-  double value, saturation;
-  double hue = ((double)(x - left) / width) * 360;
-
-  double hPos = (double)(y - top) / height;
-  if (hPos > 0.5)
-    {
-      value = 1.0 - ((hPos - 0.5) * 2);
-      saturation = 1.0;
-    }
-  else
-    {
-      value = 1.0;
-      saturation = hPos * 2;
-    }
-
-  hsv_to_rgb (hue, saturation, value, &r, &g, &b);
-
-  return (makecol (r, g, b));
-}
-
-void colorBar (ENVIRONMENT *env, int left, int top, int width, int height)
-{
-  int right = left + width;
-  int bottom = top + height;
-
-  for (int x = left; x < right; x++)
-    {
-      for (int y = top; y < bottom; y++)
-        {
-          putpixel (env->db, x, y, pickColor (left, top, width, height, x, y));
-        }
-    }
-}
-
-int textEntryBox (GLOBALDATA *global, ENVIRONMENT *env, int modify, int x, int y, char *text, unsigned int textLength)
-{
-  int ke = 0;
-  int fontWidth = text_length (font, (char *)"Z");
-  int fontHeight = text_height (font);
-  int leftX = x - (fontWidth * textLength / 2);
-  int rightX = x + (fontWidth * textLength / 2);
-  int boxWidth = fontWidth * textLength;
-//  char tempText[textLength + 1];
-  char * tempText;
-  int flashCount = 0;
-  int lx = mouse_x, ly = mouse_y;
-
-  tempText = (char *)calloc(textLength + 1, sizeof(char));
-
-  if (!tempText)
-    {
-      // Die hard!
-      cerr << "ERROR: Unable to allocate " << (textLength + 1) << " bytes in textEntryBox() !!!" << endl;
-      // exit (1);
-    }
-
-  if (! text) text = (char *)"";
-  rectfill (env->db, leftX, y - 2, rightX, y + fontHeight + 2, WHITE);
-  rect (env->db, leftX, y - 2, rightX, y + fontHeight + 2, BLACK);
-  if (!modify)
-    {
-      textout_centre_ex (env->db, font, text, x, y, BLACK, -1);
-    }
-  env->make_update (leftX - 2, y - 4, fontWidth * textLength + 4, fontHeight + 6);
-  env->do_updates ();
-
-  if (!modify)
-    {
-      free(tempText);
-      return (boxWidth);
-    }
-  strncpy (tempText, text, textLength + 1);
-
-  while (((ke >> 8) != KEY_ENTER && (ke >> 8) != KEY_ESC && (ke >> 8) != KEY_ENTER_PAD)
-         || strlen (tempText) < 1)
-    {
-      int tWidth = text_length (font, tempText);
-
-      LINUX_SLEEP;
-
-      rectfill (env->db, leftX, y - 2, rightX, y + fontHeight + 2, WHITE);
-      rect (env->db, leftX, y - 2, rightX, y + fontHeight + 2, BLACK);
-      //rectfill (screen, x - (tWidth / 2), y, x + (tWidth / 2) + 10, y + text_height (font), (flashCount < 5)?WHITE:BLACK);
-      textout_centre_ex (env->db, font, tempText, x, y, BLACK, -1);
-      rectfill (env->db, x + (tWidth / 2) + 2, y, x + (tWidth / 2) + 10, y + text_height (font), (flashCount < 25)?WHITE:BLACK);
-      env->make_update (leftX - 2, y - 4, fontWidth * textLength + 4, fontHeight + 6);
-      env->make_update (mouse_x, mouse_y, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      env->make_update (lx, ly, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      lx = mouse_x;
-      ly = mouse_y;
-      if (! global->os_mouse) show_mouse (NULL);
-
-      if (keypressed ())
-        {
-          ke = readkey ();
-        }
-      else
-        {
-          ke = 0;
-        }
-
-      if ( ((ke >> 8) == KEY_BACKSPACE) && ( strlen(tempText) > 0 ) )
-        {
-          tempText[strlen (tempText) - 1] = 0;
-          rectfill (screen, x - (tWidth / 2), y, x + (tWidth / 2) + 10, y + text_height (font), WHITE);
-          env->make_update (x - (tWidth / 2) - 2, y - 2, tWidth + 14, text_height (font) + 4);
-        }
-      else if ((ke & 0xff) >= 32 && strlen (tempText) < textLength)
-        {
-          // tempText[strlen (tempText)] = ke & 0xff;
-          tempText[strlen (tempText) + 1] = 0;
-          tempText[strlen(tempText)] = ke & 0xff;
-          //textprintf (screen, font, x + text_length (font, tempText), y, WHITE, (char *)"%c", ke & 0xff);
-        }
-      else
-        env->do_updates ();
-      if (! global->os_mouse) show_mouse (screen);
-      rest (1);
-      flashCount++;
-      flashCount = flashCount % 50;
-    }
-  if ((ke >> 8) != KEY_ESC)
-    strncpy (text, tempText, textLength);
-
-  flush_inputs ();
-
-  free(tempText);
-
-  return (boxWidth);
-}
-
-
-void credits (GLOBALDATA *global, ENVIRONMENT *env)
+/*****************************
+*** static local variables ***
+*****************************/
+static bool        allow_network    = true;
+static char        fullPath[PATH_MAX + 1] = { 0 };
+static eFullScreen full_screen      = FULL_SCREEN_EITHER;
+static bool        load_config_file = true;
+static int32_t     screen_mode      = GFX_AUTODETECT_WINDOWED;
+#ifdef NETWORK
+static int32_t     client_socket    = -1;
+#endif // NETWORK
+
+
+/*************************
+*** External variables ***
+*************************/
+extern WEAPON  weapon[WEAPONS];    // from files.cpp
+extern WEAPON  naturals[NATURALS]; // from files.cpp
+extern ITEM    item[ITEMS];        // from files.cpp
+
+
+/*****************************
+*** static local functions ***
+*****************************/
+static void    Change_Settings(bool old_sound, int32_t old_itech, int32_t old_wtech);
+static void    close_button_handler(void);
+static void    createConfig();
+static void    credits();
+static
+const  char*   do_winner();
+static void    endgame_cleanup();
+       void    init_mouse_cursor();
+static void    init_game_settings();
+static void    initialisePlayers();
+static bool    loadConfig();
+static bool    loadPlayers(FILE* file);
+static int32_t menu();
+static void    newgame();
+static int32_t parse_args(int32_t argc, char** argv);
+static void    play_demo();
+static void    play_local();
+static void    play_networked();
+static void    print_text_help();
+static void    print_text_initmsg();
+static bool    Save_Game_Settings(const char* path);
+static void    show_options();
+static void    title();
+
+
+/*****************************
+*** external functions     ***
+*****************************/
+void draw_simple_bg(bool drawImage);  // from shop.cpp
+void quickChange   (bool clearerror); // from shop.cpp
+
+
+/*******************************
+*** Function implementations ***
+*******************************/
+
+/** @brief Take care of changed settings.
+  *
+  * This function detects changes to some environment settings and, if a
+  * change has happened, makes the required changes to the game environment.
+**/
+static void Change_Settings(bool old_sound, int32_t old_itech, int32_t old_wtech)
 {
-  char dataDir[2048];
-  TEXTBLOCK *my_text;
-
-  sprintf (dataDir, "%s/credits.txt", global->dataDir);
-  my_text = new TEXTBLOCK(dataDir);
-  scrollTextList (global, env, my_text  );
-  delete my_text;
+	// first, check for a change in the sound settings
+	if (old_sound != env.sound_enabled) {
+		if (env.sound_enabled) {
+			if (detect_digi_driver(DIGI_AUTODETECT)) {
+				if (install_sound (DIGI_AUTODETECT, MIDI_NONE, NULL) < 0)
+					fprintf (stderr, "install_sound: failed turning on sound\n");
+			} else
+				fprintf (stderr, "detect_digi_driver found no sound device\n");
+		} else
+			remove_sound();
+	} // End of sound checking
+
+	// Check for tech level changes
+	if ( (old_itech != env.itemtechLevel)
+	  || (old_wtech != env.weapontechLevel) )
+		env.genItemsList();
 }
 
 
-/*
- * Save all players to a text file.
-*/
-int savePlayers_Text(GLOBALDATA *global, FILE *my_file)
+/** @brief Close Button Handler
+  *
+  * This function catches the close command, usually given by the user pressing
+  * the close window button. We'll try to clean-up.
+**/
+static void close_button_handler(void)
 {
-   int count = 0;
-  
-   while (count < global->numPermanentPlayers)
-   {
-      global->allPlayers[count]->saveToFile_Text(my_file);
-      count++;
-   } 
-   return TRUE;
+	global.pressCloseButton();
 }
 
 
-
-
-
-int loadPlayers_Text (GLOBALDATA *global, ENVIRONMENT *env, FILE *file)
+/// @brief Show the credits file in a text box
+static void credits ()
 {
-  int count, max;
-  int status = TRUE;
-  PLAYER *new_player;
-
-  max = global->numPermanentPlayers;
-  if (global->allPlayers) free(global->allPlayers);   // avoid leak
-  global->allPlayers = (PLAYER **) malloc (sizeof(PLAYER*) * max);
-  if (! global->allPlayers)
-    {
-      perror("atanks.cc: Failed to allocate memory for allPlayers in loadPlayers_Text");
-      return FALSE;
-    }
+	snprintf (path_buf, PATH_MAX, "%s/credits.txt", env.dataDir);
 
-  count = 0;
-  while (status)
-    {
-      // global->allPlayers[count] = new PLAYER(global, env, file, true);
-      new_player = new PLAYER(global, env);
-      if (! new_player)
-        {
-          perror( (char *)"atanks.cc: Failed to allocate memory for players in loadPlayers_Text");
-          return FALSE;
-        }
-      status = new_player->loadFromFile_Text(file);
-      if (status)
-        {
-          global->allPlayers[count] = new_player;
-          count++;
-          if (count == max)
-            {
-              max += 5;
-              global->allPlayers = (PLAYER**) realloc(global->allPlayers, sizeof(PLAYER *) * max);
-            }
-        }
-      else
-        // free(new_player);
-        delete new_player;
-    }     // end of while status
-
-  global->numPermanentPlayers = count;
-  return TRUE;
+	TEXTBLOCK my_text(path_buf);
+	scrollTextList (&my_text);
 }
 
 
-void newgame (GLOBALDATA *global, ENVIRONMENT *env)
+/// @brief create a fresh new config if loading was prohibited or failed
+static void createConfig()
 {
-  int objCount;
-  TANK *tank;
-
-  env->initialise ();
-  global->initialise ();
-
-  // if a game should be loaded, try it or deny loading of the game
-  if ( (global->load_game) && (!Load_Game(global, env)) )
-    global->load_game = false;
-
-  // Now check back whether to load a game
-  if (!global->load_game)
-    initialisePlayers (global);
-
-  // There must not be any tanks!
-  for (objCount = 0; (tank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && tank; objCount++)
-    {
-      tank->player = NULL; // To avoid violent death!
-      delete(tank);
-    }
-
-  // This is always true here, as a newly started game is handled like a loaded one:
-  global->bIsGameLoaded = true;
+	env.numPermanentPlayers = 0;
+
+	// Override full screen settings from command line
+	if ( (full_screen == FULL_SCREEN_TRUE)
+	  || (full_screen == FULL_SCREEN_FALSE) )
+		env.full_screen = full_screen;
+
+	// Determine basic screen settings
+	env.temp_screenWidth  = env.screenWidth;
+	env.temp_screenHeight = env.screenHeight;
+	env.halfWidth         = env.screenWidth / 2;
+	env.halfHeight        = env.screenHeight / 2;
+	env.menuBeginY        = (env.screenHeight - 400) / 2;
+	if (env.menuBeginY < 0)
+		env.menuBeginY = 0;
+	env.menuEndY = env.screenHeight - env.menuBeginY;
+
+	// Perform game initialization
+	init_game_settings ();
+	env.load_text_files();
+	init_mouse_cursor();
+
+	// At least one human player must be created
+	PLAYER *tempPlayer      = nullptr;
+	int32_t tempRes         = PE_BACK; // ePlayerEdit, player_types.h
+	char    noHumanMsg[200] = { 0 };
+
+	while (!(tempRes & PE_CONFIRM_NEW)) {
+		tempRes = new_player(&tempPlayer, 0);
+
+		if (tempPlayer) {
+			// Error case 1: The created player is an AI player
+			if (HUMAN_PLAYER != tempPlayer->type) {
+				snprintf(noHumanMsg, 199,
+				         "The player \"%s\" is no human player!",
+				         tempPlayer->getName());
+				errorMessage = noHumanMsg;
+				errorX       = env.halfWidth - text_length(font, errorMessage) / 2;
+				errorY       = env.menuBeginY + 15;
+				tempPlayer   = nullptr; // It is saved already
+				tempRes      = PE_BACK;
+			}
+		} else {
+			// error case 2: No player was created at all
+			strncpy(noHumanMsg, "Please create at least one human player!", 199);
+			errorMessage = noHumanMsg;
+			errorX       = env.halfWidth - text_length(font, errorMessage) / 2;
+			errorY       = env.menuBeginY + 15;
+			tempRes = PE_BACK;
+		}
+	} // End of force-creating a human player
+
+	// Default AI player names
+	const char* const defaultNames[] = {
+		"Caesar",
+		"Alex",
+		"Hatshepsut",
+		"Patton",
+		"Napoleon",
+		"Attila",
+		"Catherine",
+		"Hannibal",
+		"Stalin",
+		"Mao"
+	};
+
+	for (int32_t i = 0; i < 10; ++i) {
+		tempPlayer = env.createNewPlayer(defaultNames[i]);
+		tempPlayer->type = static_cast<playerType>(rand () % (LAST_PLAYER_TYPE - 1) + 1);
+		tempPlayer->generatePreferences();
+	}
 }
 
 
-
-// This function draws the background for most screens
-int draw_circlesBG (GLOBALDATA *global, BITMAP *dest, int /*x*/, int /*y*/, int /*width*/, int /*height*/, bool image)
-{
-  // int largestCircle, circleCount;
-  // BITMAP *drawTo = dest;
-
-  // try to prevent crashes on 64-bit systems by avoiding this function
-  if (! global->draw_background)
-  {
-      rectfill(dest, 0, 0, global->screenWidth - 1, global->screenHeight - 1, BLACK);
-      return 0;
-  }
-
-  if ( (image) && (global->misc[17]) )
-     stretch_blit(global->misc[17], dest, 0, 0, global->misc[17]->w, global->misc[17]->h, 0, 0,
-                  global->screenWidth, global->screenHeight);
-  else
-     rectfill(dest, 0, 0, global->screenWidth - 1, global->screenHeight - 1, DARK_GREEN);
-
-  /*
-  if (global->cacheCirclesBG)
-    {
-      if (!global->gfxData.circlesBG)
-        {
-          global->gfxData.circlesBG = create_bitmap (width, height);
-          drawTo = global->gfxData.circlesBG;
-        }
-      else
-        {
-          blit (global->gfxData.circlesBG, dest, 0, 0, 0, 0, width, height);
-          return (0);
-        }
-    }
-  else
-    {
-      drawTo = dest;
-    }
- 
-  largestCircle = (int)(global->halfWidth * (4.0/3.0));
-  if (largestCircle > 1000) largestCircle = 1000;    // perhaps avoid crash on large screens
-  global->gfxData.circle_gradient_strip = create_gradient_strip (circles_gradient, largestCircle);
-  for (circleCount = largestCircle; circleCount > 0; circleCount -= 2)
-    circlefill (drawTo, width/2, height/2, circleCount, getpixel (global->gfxData.circle_gradient_strip, 0, largestCircle - circleCount));
-
-  if (global->cacheCirclesBG)
-    draw_circlesBG (global, dest, x, y, width, height);
-  */
-  return (0);
-}
-
-ENVIRONMENT *init_game_settings (GLOBALDATA *global)
+/// @brief Draw the endgame screen and return the winner name
+/// or nullptr if no winner was found. The returned text is static
+/// and must *NOT* be freed.
+static const char* do_winner()
 {
-  int count, x, y, z;
-  ENVIRONMENT *env;
-  double expSize, disperseSize;
-  // char dataDir[2048];
-  int colour_theme = (int) global->colour_theme;
-  int status;
-
-  #ifdef WIN32
-  if (global->full_screen == FULL_SCREEN_TRUE)
-      global->os_mouse = FALSE;
-  #endif
-
-  status = allegro_init ();
-  if (status)
-  {
-    printf("Unable to start Allegro.\n");
-    exit(1);
-  }
-
-  set_window_title( "Atomic Tanks");
-  // before we get started, make sure if we are using
-  // full screen mode to ignore width and height settings
-  if (global->full_screen == FULL_SCREEN_TRUE)
-  {
-      status = get_desktop_resolution(& (global->screenWidth), & (global->screenHeight) );
-      if (status < 0)
-      {
-          global->screenWidth = 800;
-          global->screenHeight = 600;
-      }
-      screen_mode = GFX_AUTODETECT_FULLSCREEN;
-  }
-  // check for X pressed on the window bar
-  LOCK_FUNCTION(close_button_handler);
-  set_close_button_callback(close_button_handler);
-
-  if (! global->colourDepth)
-    global->colourDepth = desktop_color_depth();
-
-  if ( (global->colourDepth != 16) && (global->colourDepth != 32) )
-    global->colourDepth = 16;
-
-  set_color_depth (global->colourDepth);
-  if (global->width_override)
-  {
-    global->screenWidth = global->width_override;
-    global->halfWidth = global->screenWidth / 2;
-  }
-  if (global->height_override)
-  {
-    global->screenHeight = global->height_override;
-    global->halfHeight = global->screenHeight / 2;
-  }
-  if (set_gfx_mode (screen_mode, global->screenWidth, global->screenHeight, 0, 0) < 0)
-    {
-      perror( "set_gfx_mode");
-      // exit (1);
-      status = set_gfx_mode(screen_mode, 800, 600, 0, 0);
-      if ( status < 0 ) exit(1);
-      global->screenWidth = 800;
-      global->screenHeight = 600;
-      global->halfWidth = 400;
-      global->halfHeight = 300;
-    }
-
-#ifdef WIN32
-  if (global->full_screen == FULL_SCREEN_TRUE)
-    set_display_switch_mode(SWITCH_BACKAMNESIA);
-  else
-    set_display_switch_mode(SWITCH_BACKGROUND);
-#endif
-
-  if (install_keyboard () < 0)
-    {
-      perror ( "install_keyboard failed");
-      exit (1);
-    }
-  if (install_timer () < 0)
-    {
-      perror ( "install_timer failed");
-      exit (1);
-    }
-  if (install_mouse () < 0)
-    {
-      perror ( "install_mouse failed");
-      // exit (1);
-    }
-
-  // check to see if we want sound
-  if (global->sound > 0.0)
-  {
-    /*
-      // don't stop program if no sound since the game can be played without
-      if (install_sound (DIGI_AUTODETECT, MIDI_NONE, NULL) < 0)
-        fprintf (stderr, "install_sound: %s", allegro_error);
-    }
-    */
-        int sound_type = DIGI_AUTODETECT;
-        #ifdef LINUX
-        switch ( (int) global->sound_driver )
-        {
-        case SOUND_OSS: sound_type = DIGI_OSS; break;
-        case SOUND_ESD: sound_type = DIGI_ESD; break;
-        case SOUND_ARTS: sound_type = DIGI_ARTS; break;
-        case SOUND_ALSA: sound_type = DIGI_ALSA; break;
-        case SOUND_JACK: sound_type = DIGI_JACK; break;
-        default: sound_type = DIGI_AUTODETECT; break;
-        }
-	#endif
-        #ifdef UBUNTU
-        if (sound_type == DIGI_AUTODETECT)
-            sound_type = DIGI_OSS;
-        #endif
-        if (detect_digi_driver(sound_type)) 
-        {
-    	       if (install_sound (sound_type, MIDI_NONE, NULL) < 0)
-               {
-	           fprintf (stderr, "install_sound: failed initialising sound\n");
-                   fprintf (stderr, "Please try selecting a different Sound Driver from the Options menu.\n");
-               }
-	} 
-        else 
-		fprintf (stderr, "detect_digi_driver detected no sound device\n");
-  }      // end of we want sound
-
-  lock_cclock();
-  LOCK_VARIABLE(cclock);
-  unlock_cclock();
-  LOCK_FUNCTION(clockadd);
-  // if (install_int_ex (clockadd, BPS_TO_TIMER (FRAMES_PER_SECOND)) < 0) {
-  if (install_int_ex (clockadd, BPS_TO_TIMER(global->frames_per_second)) < 0)
-    {
-      perror ( "install_int_ex");
-      exit (1);
-    }
-  if (install_int (fpsadd, 1000) < 0)
-    {
-      perror ( "install_int");
-      exit (1);
-    }
-
-  srand (time (0));
-  WHITE = makecol (255, 255, 255);
-  BLACK = makecol (0, 0, 0);
-  PINK = makecol (255, 0, 255);
-  RED = makecol (255, 0, 0);
-  GREEN = makecol (0, 255, 0);
-  DARK_GREEN = makecol(0, 80, 0);
-  BLUE = makecol (0, 0, 255);
-  PURPLE = makecol (200, 0, 200);
-  YELLOW = makecol (255, 255, 0);
-
-  global->Load_Bitmaps();
-  global->Load_Fonts();
-
-  if (! global->os_mouse) show_mouse(NULL);
-  blit ((BITMAP *) global->title[0], screen, 0, 0, global->halfWidth - 320, global->halfHeight - 240, 640, 480);
-  if (! global->os_mouse) show_mouse (screen);
-
-  global->Load_Sounds();
-
-  for (count = 0; count < ALL_LANDS; count++)
-    global->gfxData.land_gradient_strips[count] = NULL;
-  for (count = 0; count < ALL_SKIES; count++)
-    global->gfxData.sky_gradient_strips[count] = NULL;
-
-  global->gfxData.explosion_gradient_strip = create_gradient_strip (explosion_gradients[colour_theme], 200);
-  // for (count = 0; count < 2; count++)
-  //  global->gfxData.explosion_gradient_strips[count] = create_gradient_strip (explosion_gradients[count], 200);
-
-  expSize = 0;
-  disperseSize = 0;
-  for (count = 0; count < EXPLOSIONFRAMES; count++)
-    {
-      global->gfxData.explosions[count] = create_bitmap (214, 214);
-      if (count == 0)
-        {
-          expSize = 25;
-          disperseSize = 0;
-        }
-      else if (count < EXPLODEFRAMES - 4)
-        expSize += (107 - expSize) / 3;
-      else if (count < EXPLODEFRAMES)
-        expSize--;
-      else if (count == EXPLODEFRAMES)
-        disperseSize = 25;
-      else
-        disperseSize += (107 - disperseSize) / 2;
-
-      clear_to_color (global->gfxData.explosions[count], PINK);
-      for (y = (int)expSize; y > disperseSize; y--)
-        {
-          double value;
-          value = pow ((double)y / expSize, count / 4 + 1);
-          circlefill (global->gfxData.explosions[count], 107, 107, y, getpixel (global->gfxData.explosion_gradient_strip, 0, (int)(value * 200)));
-        }
-      if (disperseSize)
-        circlefill (global->gfxData.explosions[count], 107, 107, (int)disperseSize, PINK);
-    }
-
-  expSize = 0;
-  disperseSize = 0;
-  for (count = 0; count < EXPLOSIONFRAMES; count++)
-    {
-      global->gfxData.flameFront[count] = create_bitmap (600, 30);
-      if (count == 0)
-        {
-          expSize = 10;
-          disperseSize = 0;
-        }
-      else if (count < EXPLODEFRAMES - 4)
-        expSize += (300 - expSize) / 3;
-      else if (count < EXPLODEFRAMES)
-        expSize--;
-      else if (count == EXPLODEFRAMES)
-        disperseSize = 10;
-      else
-        disperseSize += (300 - disperseSize) / 2;
-
-      clear_to_color (global->gfxData.flameFront[count], PINK);
-      for (y = (int)expSize; y > disperseSize; y--)
-        {
-          double value;
-          value = pow ((double)y / expSize, count / 4 + 1);
-          ellipsefill (global->gfxData.flameFront[count], 300, 15, y, y / 20, getpixel (global->gfxData.explosion_gradient_strip, 0, (int)(value * 200)));
-        }
-      if (disperseSize)
-        ellipsefill (global->gfxData.flameFront[count], 300, 15, (int)disperseSize, (int)disperseSize / 16, PINK);
-    }
-
-  global->gfxData.topbar = create_bitmap (global->screenWidth, MENUHEIGHT);
-  global->gfxData.topbar_gradient_strip = create_gradient_strip (topbar_gradient, 100);
-  if (!global->ditherGradients)
-    {
-      for (count = 0; count < MENUHEIGHT; count++)
-        {
-          float adjCount = (100.0 / MENUHEIGHT) * count;
-          line (global->gfxData.topbar, 0, count, global->screenWidth - 1, count, getpixel (global->gfxData.topbar_gradient_strip, 0, (int)adjCount));
-        }
-    }
-  else
-    {
-      for (x = 0; x < global->screenWidth; x++)
-        {
-          for (y = 0; y < MENUHEIGHT; y++)
-            {
-              float adjY = (100.0 / MENUHEIGHT) * y;
-              int offset;
-              if ((adjY < 2) || (adjY > 100 - 2))
-                offset = 0;
-              else
-                offset = rand () % 4 - 2;
-              putpixel (global->gfxData.topbar, x, y, getpixel (global->gfxData.topbar_gradient_strip, 0, (int)adjY + offset));
-            }
-        }
-    }
-
-  global->gfxData.stuff_bar[0] = create_bitmap (STUFF_BAR_WIDTH, STUFF_BAR_HEIGHT);
-  global->gfxData.stuff_bar[1] = create_bitmap (STUFF_BAR_WIDTH, STUFF_BAR_HEIGHT);
-  global->gfxData.stuff_icon_base = create_bitmap (STUFF_BAR_WIDTH/10, STUFF_BAR_HEIGHT);
-  clear_to_color (global->gfxData.stuff_bar[0], PINK);
-  clear_to_color (global->gfxData.stuff_bar[1], PINK);
-  clear_to_color (global->gfxData.stuff_icon_base, PINK);
-  global->gfxData.stuff_bar_gradient_strip = create_gradient_strip (stuff_bar_gradient, STUFF_BAR_WIDTH);
-  for (x = 0; x < STUFF_BAR_WIDTH; x++)
-    {
-      for (y = 0; y < STUFF_BAR_HEIGHT; y++)
-        {
-          double sides_dist = 0.1, circle_dist;
-          circle_dist = vector_length_f ((float)x-(STUFF_BAR_WIDTH - 75), (float)y - (STUFF_BAR_HEIGHT/2 - 2), 0);
-          if (circle_dist < 75)
-            circle_dist = 1 - (circle_dist / 75.0);
+	static char return_string[257] = { 0 };
+
+	// Get the dimensions of the score board and the texts right:
+	int32_t lh = env.fontHeight + 3; // The line height.
+	int32_t pd = 10; // Padding. How much space to the board border.
+
+	// Find out longest player name and score length do determine the
+	// score board size and score entry positions
+	char    head_name[5]   = "Name";
+	char    head_value[6]  = "Value";
+	char    head_score[30] = { 0 };
+	snprintf(head_score, 29, " %6s %6s %6s %6s", "Kills",
+	         "Killed", "Diff", "Won");
+
+	int32_t namLen = text_length(font, head_name);
+	int32_t valLen = text_length(font, head_value);
+	int32_t scoLen = text_length(font, head_score);
+
+	// While checking for the winner, determine the real lengths needed
+	int32_t  idx_jedi    = -1; // Jedi Player with the highest score
+	int32_t  idx_neutral = -1; // Neutral player with the highest score
+	int32_t  idx_sith    = -1; // SitH Player with the highest score
+	int32_t  idx_winner  = -1; // Player with the highest score
+	int32_t  maxdiff     = INT32_MIN;
+	int32_t  maxscore    = -1;
+	int32_t  maxkills    = -1;
+	int32_t  minkilled   = INT32_MAX;
+	bool     multiwinner = false;
+	PLAYER** players     = env.players; // short cut
+	int32_t  pl_money[MAXPLAYERS] = { 0 };
+
+	for (int32_t z = 0; z < env.numGamePlayers; z++) {
+
+		// Check the length of the name
+		int32_t curLen = text_length(font, players[z]->getName());
+		if (curLen > namLen)
+			namLen = curLen;
+
+		// Sum up weapons worth
+		for (int32_t j = 0; j < WEAPONS; ++j) {
+			if (weapon[j].amt && players[z]->nm[j])
+				pl_money[z] += (weapon[j].cost / weapon[j].amt) * players[z]->nm[j];
+		}
+
+		// Sum up items worth
+		for (int32_t j = 0; j < ITEMS; ++j) {
+			if (item[j].amt && players[z]->ni[j])
+				pl_money[z] += (item[j].cost / item[j].amt) * players[z]->ni[j];
+		}
+
+		// Check value length
+		char valTxt[16] = { 0 };
+		snprintf(valTxt, 15, " %14s", Add_Comma(pl_money[z]));
+		curLen = text_length(font, valTxt);
+		if (curLen > valLen)
+			valLen = curLen;
+
+		// Check the length of the score
+		char    scoTxt[30] = { 0 };
+		int32_t kill_diff  = players[z]->kills - players[z]->killed;
+		snprintf(scoTxt, 29, " %6d %6d %6d %6d",
+					players[z]->kills,
+					players[z]->killed,
+					kill_diff,
+					players[z]->score);
+		curLen = text_length(font, scoTxt);
+		if (curLen > scoLen)
+			scoLen = curLen;
+
+		// Determine whether a new winner or a draw situation is found
+		if ( (players[z]->score  == maxscore)
+		  && (players[z]->kills  == maxkills)
+		  && (players[z]->killed == minkilled) ) {
+			multiwinner = true;
+			if (TEAM_NEUTRAL == players[z]->team)
+				idx_neutral = z;
+		} else if ( (players[z]->score > maxscore)
+		         || ( (players[z]->score == maxscore)
+		           && (kill_diff > maxdiff) )
+		         || ( (players[z]->score == maxscore)
+		           && (kill_diff == maxdiff)
+		           && (players[z]->kills > maxkills) ) ) {
+			// Note: killed doesn't need to be checked. the same
+			// amount of kills with less killed score would mean
+			// a better diff anyway.
+			maxdiff     = kill_diff;
+			maxkills    = players[z]->kills;
+			maxscore    = players[z]->score;
+			minkilled   = players[z]->killed;
+			idx_winner  = z;
+			multiwinner = false;
+			if (TEAM_NEUTRAL == players[z]->team)
+				idx_neutral = z;
+        }
+
+		if (TEAM_JEDI == players[z]->team)
+			idx_jedi = z;
+		if (TEAM_SITH == players[z]->team)
+			idx_sith = z;
+	} // end of checking players
+
+	// Now calculate the dimensions of our score board.
+	int32_t w  = namLen + valLen + scoLen + (2 * pd);
+	int32_t h  = ((env.numGamePlayers + 4) * lh) + (2 * pd);
+	int32_t x  = env.halfWidth  - (w / 2);
+	int32_t y  = env.halfHeight - (h / 2);
+	int32_t qy = y + h + pd;
+	BOX     qarea(x + pd, qy, w - (2 * pd), env.screenHeight - pd - qy);
+
+	//stop mouse during drawing
+	SHOW_MOUSE(nullptr)
+
+	global.make_update (x, y - pd - env.misc[9]->h, w, h + pd + env.misc[9]->h);
+
+	// Draw the winning bitmap, the background and the border
+	draw_simple_bg(false);
+	draw_sprite(global.canvas, env.misc[9],
+	            env.halfWidth  - (env.misc[9]->w / 2),
+	            y - env.misc[9]->h - pd);
+	rectfill(global.canvas, x, y, x + w, y + h, BLACK);
+	rect    (global.canvas, x, y, x + w, y + h, WHITE);
+	rect    (global.canvas, x + 1, y + 1, x + w - 1, y + h - 1, GREY);
+
+	// Add the padding now, or it must be summed in everywhere!
+	x += pd;
+	y += pd;
+	w -= 2 * pd;
+	h -= 2 * pd;
+
+	// Draw winner names and info about all players
+	if (multiwinner) {
+		// check for team win
+		if ( TEAM_JEDI == players[idx_winner]->team ) {
+			if ( ( (idx_sith >= 0)
+			    && (players[idx_sith]->score    == players[idx_winner]->score) )
+			  || ( (idx_neutral >= 0)
+			    && (players[idx_neutral]->score == players[idx_winner]->score) ) )
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(48));
           else
-            circle_dist = 0;
-
-          if (x < (STUFF_BAR_HEIGHT/2 - 2))
-            sides_dist -= 0.1 - ((float)x / 150.0);
-          else if (x > STUFF_BAR_WIDTH - (STUFF_BAR_HEIGHT/2 - 2))
-            sides_dist -= ((float)(x - (STUFF_BAR_WIDTH - (STUFF_BAR_HEIGHT/2 - 2))) / 150.0);
-
-          if (y < STUFF_BAR_HEIGHT/2 - 2)
-            sides_dist -= 0.1 - ((float)(y) / 150.0);
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(45));
+		} else if ( TEAM_SITH == players[idx_winner]->team ) {
+			if ( ( (idx_jedi >= 0)
+			    && (players[idx_jedi]->score    == players[idx_winner]->score) )
+			  || ( (idx_neutral >= 0)
+			    && (players[idx_neutral]->score == players[idx_winner]->score) ) )
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(48));
           else
-            sides_dist -= ((float)(y - (STUFF_BAR_HEIGHT/2 - 2)) / 150.0);
-
-          sides_dist -= circle_dist * circle_dist;
-          if (sides_dist > ((double)x / 1000.0))
-            sides_dist = ((double)x / 1000.0);
-          if (sides_dist < 0)
-            sides_dist = 0;
-          if (circle_dist > 1)
-            circle_dist = 1;
-
-          if (x < STUFF_BAR_WIDTH/10)
-            putpixel (global->gfxData.stuff_icon_base, x, y, getpixel (global->gfxData.stuff_bar_gradient_strip, 0, (int)((sides_dist + circle_dist) * (STUFF_BAR_WIDTH-1))));
-
-          if (y < STUFF_BAR_HEIGHT - 5)
-            {
-              putpixel (global->gfxData.stuff_bar[0], x, y, getpixel (global->gfxData.stuff_bar_gradient_strip, 0, (int)((sides_dist + circle_dist) * (STUFF_BAR_WIDTH-1))));
-              putpixel (global->gfxData.stuff_bar[1], x, y, getpixel (global->gfxData.stuff_bar_gradient_strip, 0, (int)((sides_dist + circle_dist + 0.05) * (STUFF_BAR_WIDTH-1))));
-            }
-        }
-    }
-
-  if (! global->os_mouse )
-    {
-      set_mouse_sprite ((BITMAP *) global->misc[0]);
-      set_mouse_sprite_focus (0, 0);
-    }
-
-  global->window.x = 0;
-  global->window.y = 0;
-  global->window.w = 0;
-  global->window.h = 0;
-  for (z = 0; z < MAXUPDATES; z++)
-    {
-      global->updates[z].x = 0;
-      global->updates[z].y = 0;
-      global->updates[z].w = 0;
-      global->updates[z].h = 0;
-    }
-
-  env = new ENVIRONMENT (global);
-  if (!env)
-    {
-      perror ( "atanks.cc: Allocating env in init_game_settings");
-      exit (1);
-    }
-
-  clear_to_color (env->db, BLACK);
-  global->env = env;
-
-  return (env);
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(46));
+		} else
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(48));
+	} else {
+		if ( TEAM_JEDI == players[idx_winner]->team )
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(45));
+		else if (TEAM_SITH == players[idx_winner]->team)
+			snprintf(return_string, 256, "%s", env.ingame->Get_Line(46));
+		else
+			snprintf(return_string, 256, "%s: %s", env.ingame->Get_Line(47),
+					 players[idx_winner]->getName() );
+	}
+
+	// Print the title lines
+	textprintf_centre_ex(global.canvas, font, env.halfWidth, y,
+	                     players[idx_winner]->color, -1, "%s", return_string);
+
+	// to make the following easier, skip the two used lines
+	// (The title and one blank)
+	y += 2 * lh;
+
+	// Second title line, the score board header
+	int32_t valStart = x + namLen;
+	int32_t scoStart = valStart + valLen;
+	int32_t scoWidth = scoLen / 4;
+
+	textout_ex    (global.canvas, font, "Name", x, y, WHITE, -1);
+	textprintf_right_ex (global.canvas, font, valStart + valLen, y, WHITE,
+	                     -1, " %14s", "$ Value");
+	textprintf_right_ex (global.canvas, font, scoStart + (1 * scoWidth),
+						y, GREEN, -1, " %6s", "Kills");
+	textprintf_right_ex (global.canvas, font, scoStart + (2 * scoWidth),
+						y,   RED, -1, " %6s", "Killed");
+	textprintf_right_ex (global.canvas, font, scoStart + (3 * scoWidth),
+						y, WHITE, -1, " %6s", "Diff");
+	textprintf_right_ex (global.canvas, font, scoStart + (4 * scoWidth),
+						y, WHITE, -1, " %6s", "Won");
+
+	// Now get the score list:
+	sScore* score_array = sort_scores();
+
+	// And get the head entry:
+	sScore* score = score_array;
+	while (score->prev)
+		score = score->prev;
+
+
+	// Eventually the player scores can be displayed:
+	// (again skip the previous line for easier reading/doing below)
+	y   += lh;
+	int32_t z = 0;
+	while (score) {
+		textout_ex    (global.canvas, font, score->name,
+						x, y + (z * lh), score->color, -1);
+		textprintf_right_ex (global.canvas, font, valStart + valLen,
+							y + (z * lh), WHITE,
+							 -1, " %14s", Add_Comma(pl_money[score->idx]));
+		textprintf_right_ex (global.canvas, font, scoStart + (1 * scoWidth),
+							y + (z * lh), GREEN, -1, " %6d", score->kills);
+		textprintf_right_ex (global.canvas, font, scoStart + (2 * scoWidth),
+							y + (z * lh),   RED, -1, " %6d", score->killed);
+		textprintf_right_ex (global.canvas, font, scoStart + (3 * scoWidth),
+							y + (z * lh), score->diff < 0 ? RED : GREEN, -1,
+							" %6d", score->diff);
+		textprintf_right_ex (global.canvas, font, scoStart + (4 * scoWidth),
+							y + (z * lh), WHITE, -1, " %6d", score->score);
+		++z;
+		score = score->next;
+	}
+	global.do_updates();
+
+	// Add a war quote:
+	const char* quote = env.war_quotes->Get_Random_Line();
+	if (quote)
+		draw_text_in_box(&qarea, quote, false);
+
+	// Clean up
+	delete [] score_array;
+
+	return return_string;
 }
 
-int options (GLOBALDATA *global, ENVIRONMENT *env, MENUDESC *menu);
 
-int createNewPlayer (GLOBALDATA *global, ENVIRONMENT *env)
+static void endgame_cleanup()
 {
-  PLAYER *newPlayer = global->createNewPlayer (env);
-  if (!newPlayer)
-    {
-      perror ( "atanks.cc: Failed allocating memory in createNewPlayer");
-      // exit (1);
-    }
-  options (global, env, (MENUDESC*)newPlayer->menudesc);
-  return (-1);
+	while (env.numGamePlayers > 0) {
+		if (env.players[0]->tank) {
+			delete env.players[0]->tank;
+			env.players[0]->tank = nullptr;
+		}
+
+		// make sure networked clients say good-bye and return
+		// to old AI level
+		if (env.players[0]->type >= NETWORK_CLIENT)
+			env.players[0]->type =
+				env.players[0]->previous_type;
+
+		env.removeGamePlayer(env.players[0]);
+	}
+
+	global.clear_objects();
 }
 
-int destroyPlayer (GLOBALDATA *global, ENVIRONMENT *env, void *data)
-{
-  int optionsRetVal;
-  char sureMessage[200];
-  PLAYER *tempPlayer = (PLAYER*)data;
-  MENUDESC areYouSureMenu = { "Are You Sure?", 0, NULL, TRUE, TRUE};
-
-  sprintf (sureMessage, "This player (%s) will be permanently deleted", tempPlayer->getName ());
-  errorMessage = sureMessage;
-  errorX = global->halfWidth - text_length (font, errorMessage) / 2;
-  errorY = 170;
-  optionsRetVal = options (global, env, &areYouSureMenu);
-  if (optionsRetVal >> 8 != KEY_ESC)
-    {
-      global->destroyPlayer (tempPlayer);
-      return (-2);
-    }
-  return (KEY_SPACE << 8);
-}
 
-int displayPlayerName (ENVIRONMENT *env, int x, int y, void *data)
+#if defined(ATANKS_IS_MSVC) && defined(ATANKS_DEBUG)
+// this removes the keyboard and mouse handler on break events,
+// hopefully making both operational in Visual Studio again if
+// a crash was caught. It does not work on breakpoints though,
+// that doesn't trigger the Handler.
+BOOL WINAPI ctrlHandler(DWORD CtrlType)
 {
-  PLAYER *player = (PLAYER*)data;
-  char *name = player->getName ();
-  int textHeight = text_height (font);
-  int textLength = text_length (font, name);
-
-  if ((int)player->type == HUMAN_PLAYER)
-    {
-      int radius = 5;
-      circlefill (env->db, x - textLength - textHeight / 2 - 2,
-                  y + textHeight / 2,
-                  radius, makecol (200, 100, 255));
-      circle (env->db, x - textLength - textHeight / 2 - 2,
-              y + textHeight / 2,
-              radius, BLACK);
-    }
-  else
-    {
-      rectfill (env->db,
-                x - textLength - 2 - ((int)player->type * 3),
-                y + textHeight - 10,
-                x - textLength - 2,
-                y + textHeight - 2,
-                makecol (100, 255, 100));
-      rect (env->db,
-            x - textLength - 2 - ((int)player->type * 3),
-            y + textHeight - 10,
-            x - textLength - 2,
-            y + textHeight - 2,
-            BLACK);
-      for (int lineCount = 1; lineCount < player->type; lineCount++)
-        {
-          vline (env->db,
-                 x - textLength - 2 - (lineCount * 3),
-                 y + textHeight - 2,
-                 y + textHeight - 10,
-                 BLACK);
-        }
-    }
-  textout_ex (env->db, font, name, x - textLength, y, player->color, -1);
+	if ( ( CTRL_BREAK_EVENT == CtrlType )
+		|| ( CTRL_C_EVENT == CtrlType ) ) {
+		remove_mouse();
+		remove_keyboard();
+	}
 
-  return (0);
+	return FALSE;
 }
+#endif // Microsoft Visual C++ and Debug
 
 
-int options (GLOBALDATA *global, ENVIRONMENT *env, MENUDESC *menu)
-{
-  MENUENTRY *opts;
-  char my_pointer[2];
-  BUTTON *reset_button = NULL;
-  int selected_index = 0, my_key = 0;
-  int numEntries;
-  const char *title;
-  int ke, z;
-  int mouseLeftPressed;
-  char *reset_text, *back_text;
-#include "menucontent.h"
-
-     if (global->language == LANGUAGE_GERMAN)
-     {
-           reset_text = "Alles zurucksetzen";
-           back_text = "Zuruck";
-     }
-     else
-     {
-           reset_text = "Reset All";
-           back_text = "Back";
-     }
-
-  if (!menu)
-  {
-    menu = &mainMenu;
-    reset_button = new BUTTON(global, env, global->halfWidth - 5 - text_length (font, menu->title), global->menuBeginY + 70, // 170, 
-                    reset_text, (BITMAP *) global->misc[7], (BITMAP *) global->misc[7], 
-                    (BITMAP *) global->misc[8]);
-  }
-
-  opts = menu->entries;
-  numEntries = menu->numEntries;
-  title = menu->title;
-
-  char name_buff[64] = {0x0};
-  char format_buff[64] = {0x0};
-  int done, lb;
-  int stop = 0;
-
-  int *updateoption;
-
-  updateoption = (int *)calloc(numEntries, sizeof(int));
-  if (!updateoption)
-    {
-      // Die hard!
-      cerr << "ERROR: Unable to allocate " << numEntries << " bytes in options() !!!" << endl;
-      // exit (1);
-    }
-
-  BUTTON *but_okay = NULL, *but_quit = NULL;
-  if (menu->okayButton)
-    {
-      int xpos = global->halfWidth - 80;
-      if (menu->quitButton)
-        xpos -= 80;
-      // but_okay = new BUTTON (global, env, xpos, global->halfHeight + 160, "Okay", (BITMAP*)global->misc[7], (BITMAP*)global->misc[7], (BITMAP*)global->misc[8]);
-      but_okay = new BUTTON (global, env, xpos, global->menuBeginY + 360 , "Okay", (BITMAP*)global->misc[7], (BITMAP*)global->misc[7], (BITMAP*)global->misc[8]);
-      if (!but_okay)
-        {
-          perror ( "atanks.cc: Failed allocating memory for but_okay in options");
-          // exit (1);
-        }
-    }
-  if (menu->quitButton)
-    {
-      int xpos = global->halfWidth - 80;
-      if (menu->okayButton)
-        xpos += 80;
-      but_quit = new BUTTON (global, env, xpos, global->menuBeginY + 360, back_text, (BITMAP*)global->misc[7], (BITMAP*)global->misc[7], (BITMAP*)global->misc[8]);
-      // but_quit = new BUTTON (global, env, xpos, global->halfHeight + 160, back_text, (BITMAP*)global->misc[7], (BITMAP*)global->misc[7], (BITMAP*)global->misc[8]);
-      if (!but_quit)
-        {
-          perror ( "atanks.cc: Failed allocating memory for but_quit in options");
-          // exit (1);
-        }
-    }
-
-  lock_cclock();
-  mouseLeftPressed = done = lb = env->mouseclock = cclock = 0;
-  unlock_cclock();
-  fi = 1;
-
-  for (z = 0; z < numEntries; z++)
-    {
-      updateoption[z] = 1;
-    }
-
-  flush_inputs ();
-
-  do
-    {
-      LINUX_SLEEP;
-      while (get_cclock() > 0)
-        {
-          lock_cclock();
-          cclock--;
-          unlock_cclock();
-          my_key = 0;
-          if ( keypressed() )
-          {
-             my_key = readkey();
-             my_key = my_key >> 8;
-             if (my_key == KEY_DOWN)
-             {
-                selected_index++;
-                if (selected_index >= numEntries)
-                   selected_index = 0;
-                my_key = 0;
-             }
-             else if (my_key == KEY_UP)
-             {
-                 selected_index--;
-                 if (selected_index < 0)
-                   selected_index = numEntries - 1;
-                 my_key = 0;
-             }
-             else if (my_key == KEY_ENTER_PAD)
-                  my_key = KEY_ENTER;
-
-             for (z = 0; z < numEntries; z++)
-                updateoption[z] = TRUE;
-             fi = TRUE;
-             
-          }     // end of a key was pressed
-
-          if (!lb && mouse_b & 1)
-            {
-              env->mouseclock = 0;
-              mouseLeftPressed = 1;
-            }
-          else
-            {
-              mouseLeftPressed = 0;
-            }
-          lb = (mouse_b & 1) ? 1 : 0;
-          if ( ((mouse_b & 1 || mouse_b & 2) && !env->mouseclock) || (my_key) )
-            {
-              for (z = 0; z < numEntries; z++)
-                {
-                  int midX = opts[z].x;
-                  int midY = opts[z].y;
-                  if (opts[z].type == OPTION_MENUTYPE)
-                    {
-                      sprintf (name_buff, "-> %s", opts[z].name);
-                      if ( ((!opts[z].viewonly) && mouse_x > midX - text_length (font, name_buff) && mouse_x < midX && mouse_y >= midY && mouse_y < midY + 10) || 
-                         ( (my_key == KEY_SPACE) && (selected_index == z)) )
-                        {
-                          int optsRetVal = options (global, env, (MENUDESC*)opts[z].value);
-                          if (optsRetVal < 0)
-                            {
-                              return (optsRetVal + 1);
-                            }
-                          fi = 1;
-                          for (z = 0; z < numEntries; z++)
-                            {
-                              updateoption[z] = 1;
-                            }
-                          my_key = selected_index = 0;
-                        }
-                    }
-                  else if (opts[z].type == OPTION_ACTIONTYPE)
-                    {
-                      sprintf (name_buff, "-> %s", opts[z].name);
-                      if ( ((!opts[z].viewonly) && mouse_x > midX - text_length (font, name_buff) && mouse_x < midX && mouse_y >= midY && mouse_y < midY + 10) ||
-                           ( (my_key == KEY_SPACE) && (selected_index == z) ) )
-                        {
-                          int (*action) (GLOBALDATA*, ENVIRONMENT*, void*)
-                          = (int (*)(GLOBALDATA*, ENVIRONMENT*, void*))opts[z].value;
-                          int actionRetVal = action (global, env, opts[z].data);
-                          if (actionRetVal)
-                            return (actionRetVal);
-                        }
-                    }
-                  else if (opts[z].type == OPTION_TEXTTYPE)
-                    {
-                      int my_text_length;
-                      strcmp(opts[z].name, "Name") ? my_text_length = 11 : my_text_length = NAME_LENGTH;
-                      int boxWidth;
-                      if (! strcmp(opts[z].name, "Server address") )
-                         my_text_length = ADDRESS_LENGTH;
-
-                      if (my_text_length == NAME_LENGTH)
-                        boxWidth = textEntryBox (global, env, FALSE, midX + 100, midY, (char*)opts[z].value, my_text_length);
-                      else if (my_text_length == ADDRESS_LENGTH)
-                        boxWidth = textEntryBox(global, env, FALSE, midX + 70, midY, (char*) opts[z].value, my_text_length);
-                      else
-                        boxWidth = textEntryBox (global, env, FALSE, midX + 50, midY, (char*)opts[z].value, my_text_length);
-                      if ( ((!opts[z].viewonly) && mouse_x > midX - text_length (font, name_buff) && mouse_x < midX + 50 + boxWidth && mouse_y >= midY && mouse_y < midY + 10) ||
-                           ( (selected_index == z) && (my_key == KEY_SPACE) ) )
-                        {
-                          if (my_text_length == NAME_LENGTH)
-                            textEntryBox (global, env, TRUE, midX + 100, midY, (char*)opts[z].value, my_text_length);
-                          else if (my_text_length == ADDRESS_LENGTH)
-                            textEntryBox(global, env, TRUE, midX + 70, midY, (char *)opts[z].value, my_text_length);
-                          else
-                            textEntryBox (global, env, TRUE, midX + 50, midY, (char*)opts[z].value, my_text_length);
-                          updateoption[z] = 1;
-                        }
-                    }
-                  else if (opts[z].type == OPTION_COLORTYPE)
-                    {
-                      if ((!opts[z].viewonly) && mouse_x > midX && mouse_x < midX + 100 && mouse_y >= midY && mouse_y < midY + 15)
-                        {
-                          *(int*)opts[z].value = pickColor (midX, midY, 100, 15, mouse_x, mouse_y);
-                          updateoption[z] = 1;
-                        }
-                      colorBar (env, midX, midY, 100, 15);
-                      rectfill (env->db, midX + 110, midY, midX + 130, midY + 10, *(int*)opts[z].value);
-                      rect (env->db, midX + 110, midY, midX + 130, midY + 10, BLACK);
-                    }
-                  else if (opts[z].type == OPTION_TOGGLETYPE)
-                    {
-                      int tlen = text_length (font, name_buff);
-                      int thgt = text_height (font);
-                      int temp_counter;
-                      if ( (mouseLeftPressed && (!opts[z].viewonly) && mouse_x > midX - tlen / 2 && mouse_x < midX + tlen / 2 && mouse_y >= midY && mouse_y < midY + thgt) ||
-                         ( (my_key == KEY_SPACE) && (selected_index == z) ) )
-                        {
-                          if (*opts[z].value == 0)
-                            *opts[z].value = 1;
-                          else
-                            *opts[z].value = 0;
-                          mouseLeftPressed = 1;
-                          // updateoption[z] = 1;
-                          // Crude, but hopefulyl useful
-                          for (temp_counter = 0; temp_counter < numEntries; temp_counter++)
-                               updateoption[temp_counter] = 1;
-                        }
-                    }
-                  else
-                    {
-                      if (!opts[z].viewonly)
-                        {
-                          if (mouse_x >= midX + 100 && mouse_x < midX + 110 && mouse_y >= midY && mouse_y < midY + 10)
-                            {
-                              if (mouse_b & 1)
-                                *opts[z].value -= opts[z].increment;
-                              else if (mouse_b & 2)
-                                *opts[z].value -= opts[z].increment * 10;
-                              updateoption[z] = 1;
-                            }
-                          if (mouse_x >= midX + 112 && mouse_x < midX + 122 && mouse_y >= midY && mouse_y < midY + 10)
-                            {
-                              if (mouse_b & 1)
-                                *opts[z].value += opts[z].increment;
-                              else if (mouse_b & 2)
-                                *opts[z].value += opts[z].increment * 10;
-                              updateoption[z] = 1;
-                            }
-
-                          if ( (my_key == KEY_RIGHT) && (selected_index == z) )
-                          {
-                              *opts[z].value += opts[z].increment;
-                              updateoption[z] = 1;
-                          }
-                          else if ( (my_key == KEY_LEFT) && (selected_index == z) )
-                          {
-                              *opts[z].value -= opts[z].increment;
-                              updateoption[z] = 1;
-                          }
-
-                          // This if block is a nasty hack to get the tank
-                          // styles to redraw on the Players menu.
-                          if (updateoption[z])
-                          {
-                              int my_counter;
-                              for (my_counter = 0; my_counter < numEntries; my_counter++)
-                                 updateoption[my_counter] = TRUE;
-                              fi = TRUE;
-                          }
-                          /*if (mouse_x >= midX + 134 && mouse_x < midY + 154 && mouse_y >= midY && mouse_y < midY + 10) {
-                          	*opts[z].value = opts[z].defaultv;
-                          	updateoption[z] = 1;
-                          }*/
-                          if (*opts[z].value > opts[z].max)
-                            {
-                              *opts[z].value = opts[z].min;
-                            }
-                          if (*opts[z].value < opts[z].min)
-                            {
-                              *opts[z].value = opts[z].max;
-                            }
-                        }
-                    }
-                }
-            }
-          env->mouseclock++;
-          if (env->mouseclock > 10)
-            {
-              env->mouseclock = 0;
-            }
-        }
-
-      env->make_update (mouse_x, mouse_y, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      env->make_update (lx, ly, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      lx = mouse_x;
-      ly = mouse_y;
-      if (! global->os_mouse) show_mouse (NULL);
-
-      // draw menu stuff
-      if (fi)
-        {
-          drawMenuBackground (global, env, BACKGROUND_BLANK, rand (), 400);
-          textout_ex (env->db, font, title, global->halfWidth - 3 - text_length (font, title), global->menuBeginY + 50, BLACK, -1);
-          textout_ex (env->db, font, title, global->halfWidth - 5 - text_length (font, title), global->menuBeginY + 50, WHITE, -1);
-          for (z = 0; z < numEntries; z++)
-            {
-              int midX = opts[z].x;
-              int midY = opts[z].y;
-
-              if (z == selected_index)
-                  strcpy(my_pointer, "*");
-              else
-                  my_pointer[0] = '\0';
-
-              if (opts[z].type == OPTION_TOGGLETYPE)
-                {
-                  int color = (*opts[z].value)?WHITE:BLACK;
-                  int radius = text_length(font, opts[z].name) / 2;
-                  int y_radius = text_height(font);
-                  if (y_radius > 8)
-                      y_radius = 8;
-                  ellipsefill (env->db, midX, midY + text_height (font) / 2, radius, y_radius, color);
-                  textout_ex(env->db, font, my_pointer, (midX - radius) - 40 , midY, WHITE, -1);
-                }
-
-              if (opts[z].displayFunc)
-                {
-                  if (opts[z].type == OPTION_TOGGLETYPE)
-                  {
-                      opts[z].displayFunc (env,
-                                           midX + text_length (font, opts[z].name) / 2, midY,
-                                           opts[z].data);
-                  }
-                  else if (opts[z].type == OPTION_MENUTYPE)
-                  {
-                      opts[z].displayFunc (env,
-                                           midX, midY,
-                                           opts[z].data);
-                      sprintf(name_buff, "%s", my_pointer);
-                      textout_ex(env->db, font, name_buff, midX - 125, midY, WHITE, -1);
-                  }
-                  else if (opts[z].type == OPTION_SPECIALTYPE)
-                  {
-                      opts[z].displayFunc(env, 
-                                          midX + text_length(font, opts[z].name) / 2, midY,
-                                          opts[z].value );
-                      sprintf (name_buff, "%s %s:", my_pointer, opts[z].name);
-                      textout_ex (env->db, font, name_buff, midX - text_length (font, name_buff), 
-                                  midY, opts[z].color, -1);
-                      if (!opts[z].viewonly)
-                      {
-                          draw_sprite_v_flip (env->db, (BITMAP *) global->misc[6], midX + 100, midY);
-                          draw_sprite (env->db, (BITMAP *) global->misc[6], midX + 112, midY);
-                      }
-                  }
-                }
-              else if (opts[z].type == OPTION_MENUTYPE)
-                {
-                  sprintf (name_buff, "%s -> %s", my_pointer, opts[z].name);
-                  textout_ex (env->db, font, name_buff, midX - text_length (font, name_buff), midY, opts[z].color, -1);
-                }
-              else if (opts[z].type == OPTION_ACTIONTYPE)
-                {
-                  sprintf (name_buff, "%s -> %s", my_pointer, opts[z].name);
-                  textout_ex (env->db, font, name_buff, midX - text_length (font, name_buff), midY, opts[z].color, -1);
-                }
-              else if (opts[z].type == OPTION_TEXTTYPE)
-                {
-                  sprintf (name_buff, "%s %s:", my_pointer, opts[z].name);
-                  textout_ex (env->db, font, name_buff, midX - text_length (font, name_buff), midY, opts[z].color, -1);
-                }
-              else if (opts[z].type == OPTION_COLORTYPE)
-                {
-                  sprintf (name_buff, "%s %s:", my_pointer, opts[z].name);
-                  textout_ex (env->db, font, name_buff, midX - text_length (font, name_buff), midY, opts[z].color, -1);
-                }
-              else if (opts[z].type == OPTION_TOGGLETYPE)
-                {
-                  sprintf (name_buff, "%s %s", my_pointer, opts[z].name);
-                  textout_centre_ex (env->db, font, name_buff, midX, midY, opts[z].color, -1);
-                }
-              else
-                {
-                  sprintf (name_buff, "%s %s:", my_pointer, opts[z].name);
-                  textout_ex (env->db, font, name_buff, midX - text_length (font, name_buff), midY, opts[z].color, -1);
-                  if (!opts[z].viewonly)
-                    {
-                      draw_sprite_v_flip (env->db, (BITMAP *) global->misc[6], midX + 100, midY);
-                      draw_sprite (env->db, (BITMAP *) global->misc[6], midX + 112, midY);
-                    }
-                }
-
-                if (my_pointer[0])
-                     env->make_update(midX - 200, midY - 20, 400, 40);
-
-            }        // end of for loop
-          if (but_okay) but_okay->draw (env->db);
-          if (but_quit) but_quit->draw (env->db);
-          if (reset_button) reset_button->draw(env->db);
-        }      // end of if fi
-
-      for (z = 0; z < numEntries; z++)
-        {
-          int midX = opts[z].x;
-          int midY = opts[z].y;
-          if (updateoption[z])
-            {
-              updateoption[z] = 0;
-              if (opts[z].type == OPTION_TOGGLETYPE)
-                {
-                  int color = (*opts[z].value)?WHITE:BLACK;
-                  int text_tall = text_height(font);
-                  if (text_tall > 8)
-                      text_tall = 8;
-                  ellipsefill (env->db, midX, midY + text_height (font) / 2, text_length (font, opts[z].name) / 2, text_tall, color);
-                }
-              if (opts[z].displayFunc)
-                {
-                  if (opts[z].type == OPTION_TOGGLETYPE)
-                    {
-                      opts[z].displayFunc (env,
-                                           midX + text_length (font, opts[z].name) / 2, midY,
-                                           opts[z].data);
-                    }
-                  else if (opts[z].type == OPTION_MENUTYPE)
-                    {
-                      opts[z].displayFunc (env,
-                                           midX, midY,
-                                           opts[z].data);
-                    }
-                  env->make_update (midX - 200, midY - text_height (font), 450, 50);
-                  env->do_updates();
-                }
-              else if (opts[z].type != OPTION_MENUTYPE && opts[z].type != OPTION_ACTIONTYPE)
-                {
-                  if (opts[z].type == OPTION_DOUBLETYPE)
-                    {
-                      sprintf (format_buff, opts[z].format, *opts[z].value);
-                      textEntryBox (global, env, FALSE, midX + 50, midY, format_buff, 11);
-                    }
-                  else if (opts[z].type == OPTION_TEXTTYPE)
-                    {
-                      textEntryBox (global, env, FALSE, midX + 100, midY, (char*)opts[z].value, NAME_LENGTH);
-                    }
-                  else if (opts[z].type == OPTION_COLORTYPE)
-                    {
-                      colorBar (env, midX, midY, 100, 15);
-                      rectfill (env->db, midX + 110, midY, midX + 130, midY + 10, *(int*)opts[z].value);
-                      rect (env->db, midX + 110, midY, midX + 130, midY + 10, BLACK);
-                    }
-                  else if (opts[z].type == OPTION_TOGGLETYPE)
-                    {
-                      sprintf (format_buff, "%s", opts[z].name);
-                      textout_centre_ex (env->db, font, format_buff, midX, midY, opts[z].color, -1);
-                    }
-                  else if (opts[z].specialOpts)
-                    {
-                      textEntryBox (global, env, FALSE, midX + 50, midY, opts[z].specialOpts[(int) *opts[z].value], 11);
-                    }
-                  env->make_update (midX - 100, midY - 2, 250, 20);
-                }
-            }
-        }
-
-      if (fi)
-        {
-          fi = 0;
-          quickChange (global, env->db);
-        }
-
-      if ( (but_quit && but_quit->isPressed()) || (my_key == KEY_ESC) )
-        {
-          global->wr_lock_command();
-          global->command = GLOBAL_COMMAND_MENU;
-          global->unlock_command();
-          stop = 1;
-        }
-      if ( (but_okay && but_okay->isPressed()) || (my_key == KEY_ENTER) )
-        {
-          stop = 2;
-        }
-
-      if ( (reset_button) && (reset_button->isPressed() ) )
-      {
-         // RESET all options!
-         env->Reset_Options();
-         global->Reset_Options();
-      }
-
-      if (but_okay) but_okay->draw (env->db);
-      if (but_quit) but_quit->draw (env->db);
-      if (reset_button) reset_button->draw(env->db);
-
-      if (! global->os_mouse) show_mouse(env->db);
-      if (global->close_button_pressed) stop = 1;
-      env->do_updates ();
-    }
-  // while ((!keypressed ()) && (!stop) );
-  while (! stop);
-
-  if (!stop)
-    ke = readkey ();
-  else if (stop == 2)
-    ke = KEY_ENTER << 8;
-  else
-    ke = KEY_ESC << 8;
-
-  flush_inputs();
-
-  if (but_quit)
-    delete but_quit;
-  if (but_okay)
-    delete but_okay;
-
-  if ( reset_button )
-    delete reset_button;
-
-  free(updateoption);
-
-  return (ke);
-}
-
-int editPlayers (GLOBALDATA *global, ENVIRONMENT *env)
+void init_mouse_cursor()
 {
-  int optionsRetVal;
-  // int rows = (global->screenHeight - 400) / 15;
-  int rows = (global->screenHeight - 2 * global->menuBeginY - 200) / 15;
-  int columns = (global->numPermanentPlayers / rows) + 1;
-  rows = (rows / columns) + 1;
-
-  MENUENTRY *playersOpts;
-  MENUDESC playersMenu;
-  // playersOpts = new MENUENTRY[1 + global->numPermanentPlayers];
-  playersOpts = (MENUENTRY *) calloc( global->numPermanentPlayers + 1, sizeof(MENUENTRY));
-  if (!playersOpts)
-    {
-      perror ( "atanks.cc: Failed allocating memory for playersOpts in editPlayers");
-      return 0;
-      // exit (1);
-    }
-  playersOpts[0].name = "Create New";
-  playersOpts[0].displayFunc = NULL;
-  playersOpts[0].color = WHITE;
-  playersOpts[0].value = (double*)createNewPlayer;
-  playersOpts[0].data = NULL;
-  playersOpts[0].type = OPTION_ACTIONTYPE;
-  playersOpts[0].viewonly = FALSE;
-  playersOpts[0].x = global->halfWidth - 3;
-  // playersOpts[0].y = global->halfHeight - 68 - 15;
-  playersOpts[0].y = global->menuBeginY + 117;
-
-  playersMenu.title = "Players";
-  playersMenu.numEntries = 1 + global->numPermanentPlayers;
-  playersMenu.entries = playersOpts;
-  playersMenu.quitButton = TRUE;
-  playersMenu.okayButton = FALSE;
-
-  for (int count = 0; count < global->numPermanentPlayers; count++)
-    {
-      MENUENTRY *opt = &playersOpts[1 + count];
-
-      opt->name = global->allPlayers[count]->getName ();
-      opt->displayFunc = displayPlayerName;
-      opt->data = global->allPlayers[count];
-      opt->color = global->allPlayers[count]->color;
-      opt->value = (double*)global->allPlayers[count]->menudesc;
-      opt->type = OPTION_MENUTYPE;
-      opt->viewonly = FALSE;
-      opt->x = global->halfWidth - (((count % columns) - (columns / 2)) * 90) - (((columns + 1) % 2) * 45);
-      // opt->y = global->halfHeight - 68 + ((count / columns) * 15);
-      opt->y = global->menuBeginY + 132 + ((count / columns) * 15);
-    }
-  optionsRetVal = options (global, env, &playersMenu);
-
-  // delete playersOpts;
-  free(playersOpts);
-
-  return (optionsRetVal);
+	if (env.osMouse )
+		show_os_cursor(MOUSE_CURSOR_ARROW);
+	else {
+		set_mouse_sprite (env.misc[0]);
+		set_mouse_sprite_focus (0, 0);
+	}
 }
 
-int selectPlayers (GLOBALDATA *global, ENVIRONMENT *env)
-{
-  MENUENTRY roundOpt = { global->ingame->complete_text[1], NULL, WHITE, (double*)&global->rounds, NULL, "%2.0f", 1, MAX_ROUNDS, 1, 5, NULL,
-                         OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->menuBeginY + 82
-                       };
-  MENUENTRY gamename = { global->ingame->complete_text[2], NULL, WHITE, (double *) global->game_name, NULL, "%s", 0, 0, 0, 0, NULL,
-                         OPTION_TEXTTYPE, FALSE, global->halfWidth - 3, global->menuBeginY + 100
-                       };
-  MENUENTRY loadgame, campaign;
-  int save_game_exists;
-
-  int optionsRetVal, z;
-  // int rows = (global->screenHeight - 400) / 15;
-  int rows = (global->screenHeight - 2 * global->menuBeginY - 200) / 15;
-  int columns = (global->numPermanentPlayers / rows) + 1;
-  int playerCount = 0;
-
-  int number_saved_games = 0;
-  struct dirent **saved_game_names;
-  char **game_list = NULL;
-  MENUENTRY *playersOpts;
-  MENUDESC playersMenu;
-  MENUENTRY load_game_entry;
-
-  // find saved games
-  saved_game_names = Find_Saved_Games(global, &number_saved_games);
-  if ( (saved_game_names) && ( number_saved_games ) )
-    {
-      int count;
-
-      // move the names into a char list
-      game_list = (char **) calloc( number_saved_games, sizeof(char *) );
-      for (count = 0; count < number_saved_games; count++)
-        {
-          game_list[count] = saved_game_names[count]->d_name;
-          // clear trailign extension
-          if ( strchr(game_list[count], '.') )
-            strchr(game_list[count], '.')[0] = '\0';
-        }
-
-      global->saved_game_list = game_list;
-      // set up menu for selecting saved games
-      load_game_entry.name = global->ingame->complete_text[3];
-      load_game_entry.displayFunc = NULL;
-      load_game_entry.color = WHITE;
-      load_game_entry.value = (double *) &global->saved_game_index;
-      load_game_entry.data = NULL;
-      load_game_entry.format = "%s";
-      load_game_entry.min = 0;
-      load_game_entry.max = number_saved_games - 1;
-      load_game_entry.increment = 1;
-      load_game_entry.defaultv = 0;
-      load_game_entry.specialOpts = global->saved_game_list;
-      load_game_entry.type = OPTION_SPECIALTYPE;
-      load_game_entry.viewonly = FALSE;
-      load_game_entry.x = global->halfWidth;
-      load_game_entry.y = global->menuBeginY + 120;
-    }
-
-  rows = (rows / columns) + 1;
-  // playersOpts = new MENUENTRY[global->numPermanentPlayers + 4];
-  playersOpts = (MENUENTRY *) calloc( global->numPermanentPlayers + 5, sizeof(MENUENTRY) );
-  if (!playersOpts)
-    {
-      perror ( "atanks.cc: Failed allocating memory for playersOpts in selectPlayers");
-      // exit (1);
-    }
-
-  loadgame.name = global->ingame->complete_text[4];
-  loadgame.displayFunc = NULL;
-  loadgame.data = &global->load_game;
-  loadgame.color = WHITE;
-  loadgame.value = (double *) &global->load_game;
-  loadgame.type = OPTION_TOGGLETYPE;
-  loadgame.viewonly = FALSE;
-  loadgame.x = global->halfWidth - 50;
-  loadgame.y = global->menuBeginY + 140;
-
-  campaign.name = global->ingame->complete_text[5];
-  campaign.displayFunc = NULL;
-  campaign.data = &global->campaign_mode;
-  campaign.color = WHITE;
-  campaign.value = (double *) &global->campaign_mode;
-  campaign.type = OPTION_TOGGLETYPE;
-  campaign.viewonly = FALSE;
-  campaign.x = global->halfWidth + 50;
-  campaign.y = global->menuBeginY + 140;
-
-  playersMenu.title = global->ingame->complete_text[0];
-  playersMenu.numEntries = global->numPermanentPlayers + 5;
-  playersMenu.entries = playersOpts;
-  playersMenu.quitButton = TRUE;
-  playersMenu.okayButton = TRUE;
-
-  for (int count = 0; count < global->numPermanentPlayers; count++)
-    {
-      MENUENTRY *opt = &playersOpts[count];
-
-      opt->name = global->allPlayers[count]->getName ();
-      opt->displayFunc = displayPlayerName;
-      opt->data = global->allPlayers[count];
-      opt->color = global->allPlayers[count]->color;
-      opt->value = (double*)&global->allPlayers[count]->selected;
-      opt->type = OPTION_TOGGLETYPE;
-      opt->viewonly = FALSE;
-      opt->x = global->halfWidth - (((count % columns) - (columns / 2)) * 90) - (((columns + 1) % 2) * 45);
-      // opt->y = 265 + ( (count / columns) * 15 );
-      opt->y = global->menuBeginY + 165 + ( (count / columns) * 15);
-    }
-  memcpy (&playersOpts[global->numPermanentPlayers], &roundOpt, sizeof (MENUENTRY));
-  memcpy (&playersOpts[global->numPermanentPlayers + 1], &gamename, sizeof (MENUENTRY));
-  memcpy (&playersOpts[global->numPermanentPlayers + 2], &loadgame, sizeof (MENUENTRY));
-  memcpy (&playersOpts[global->numPermanentPlayers + 3], &campaign, sizeof (MENUENTRY));
-  if ((number_saved_games) && (game_list) )
-    memcpy (&playersOpts[global->numPermanentPlayers + 4], &load_game_entry, sizeof(MENUENTRY));
-
-  do
-    {
-      optionsRetVal = options (global, env, &playersMenu);
-      if ( global->load_game )
-        {
-          if ( ( global->saved_game_list) && ( global->saved_game_list[ (int) global->saved_game_index ][0] ) )
-            {
-              memset(global->game_name, '\0', GAME_NAME_LENGTH);
-              strncpy(global->game_name, global->saved_game_list[ (int) global->saved_game_index ], 16);
-            }
-        }
-
-      if (optionsRetVal >> 8 == KEY_ENTER)
-        {
-          if (! global->load_game )    // trying to play a game
-            {
-              playerCount = 0;
-              global->numPlayers = 0;
-              for (z = 0; z < global->numPermanentPlayers; z++)
-                {
-                  if (global->allPlayers[z]->selected)
-                    {
-                      global->addPlayer (global->allPlayers[z]);
-                      playerCount++;
-                    }
-                }
-              if ((playerCount < 2) || (playerCount > MAXPLAYERS))
-                {
-                  if (playerCount < 2)
-                    errorMessage = global->ingame->complete_text[8];
-                  else if (playerCount > MAXPLAYERS)
-                    errorMessage = global->ingame->complete_text[9];
-                  errorX = global->halfWidth - text_length (font, errorMessage) / 2;
-                  errorY = global->menuBeginY + 70;
-                  optionsRetVal = 0;
-                }
-              else
-                {
-                  optionsRetVal = PLAY_GAME;
-                }
-            }    // end of trying to start a new game
-          else     // start to load an existing game
-            {
-              save_game_exists = Check_For_Saved_Game(global);
-              if (save_game_exists)
-                optionsRetVal = LOAD_GAME;
-              else
-                {
-                  optionsRetVal = 0;
-                  errorMessage = global->ingame->complete_text[39];
-                  errorX  = global->halfWidth - text_length(font, errorMessage) / 2;
-                  errorY = global->menuBeginY + 70;
-                }
-            }
-        }
-      // zero means an error occured.
-      // keep running the loop until ESC is pressed or a non-zero value appears
-    }
-  // while (optionsRetVal == 0);
-  while ( (optionsRetVal >> 8 != KEY_ESC) && (optionsRetVal != PLAY_GAME) &&
-          (optionsRetVal != LOAD_GAME) );
-
-  // delete playersOpts;
-  free(playersOpts);
-  if (optionsRetVal >> 8 == KEY_ESC)
-    optionsRetVal = ESC_MENU;
-
-  if (saved_game_names) free(saved_game_names);
-
-  return (optionsRetVal);
-}
 
-void title (GLOBALDATA *global)
+static void init_game_settings()
 {
-  if (! global->os_mouse) show_mouse(NULL);
-  blit ((BITMAP *) global->title[0], screen, 0, 0, global->halfWidth - 320, global->halfHeight - 240, 640, 480);
-  if (! global->os_mouse) show_mouse (screen);
-  clear_keybuf ();
+	if (env.full_screen == FULL_SCREEN_TRUE)
+		env.osMouse = false;
 
-  //wait_for_input();
+	int32_t status = allegro_init();
 
-  if (! global->os_mouse) show_mouse (NULL);
-}
-
-
-
-
-int menu (GLOBALDATA *global, ENVIRONMENT *env)
-{
-  int ban, anclock, lb, updateplayers, done, updaterounds, z;
-  int move_text;
-  int seconds_idle = 0;
-  int current_index = 0, max_index = 7;
-  int my_key = 0;
-  int bn = 0;        // button number
-  int shift_menu = global->halfHeight - 240;
-  if (shift_menu < 0) shift_menu = -shift_menu;
-  else shift_menu = 0;
-
-  move_text = 75;
-  if (global->language == LANGUAGE_RUSSIAN)
-      bn += MENUBUTTONS * 2;
-
-  BUTTON but_play(global, env, global->halfWidth - move_text, global->halfHeight - 235 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-  bn += 2;
-  BUTTON but_help(global, env, global->halfWidth - move_text, global->halfHeight - 185 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-  bn += 2;
-  BUTTON but_options(global, env, global->halfWidth - move_text, global->halfHeight - 135 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-  bn += 2;
-  BUTTON but_players(global, env, global->halfWidth - move_text, global->halfHeight - 85 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-  bn += 2;
-  BUTTON but_credits(global, env, global->halfWidth - move_text, global->halfHeight - 35 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-  bn += 2;
-  BUTTON but_quit(global, env, global->halfWidth - move_text, global->halfHeight + 65 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-  bn += 2;
-  BUTTON but_network(global, env, global->halfWidth - move_text, global->halfHeight + 15 + shift_menu, NULL, (BITMAP*)global->button[bn], (BITMAP*)global->button[bn], (BITMAP*)global->button[bn+1]);
-
-  BUTTON *button[MENUBUTTONS] = {&but_play, &but_help, &but_options,
-                                 &but_players, &but_credits, &but_network, &but_quit
-                                };
-
-  ban = -1;
-  lock_cclock();
-  cclock = global->updateCount = lx = ly = anclock = env->mouseclock = 0;
-  unlock_cclock();
-  lb = updateplayers = done = updaterounds = 0;
-  fi = global->stopwindow = 1;
-  while (!done)
-    {
-      if ( global->Check_Time_Changed() )
-      {
-         seconds_idle++;
-         if (seconds_idle > DEMO_WAIT_TIME)
-         {
-            done = 1;
-            global->wr_lock_command();
-            global->command = GLOBAL_COMMAND_DEMO;
-            global->unlock_command();
-         }
-      }   
-
-      // LINUX_SLEEP;
-      LINUX_REST;
-      while (get_cclock() > 0)
-        {
-          lock_cclock();
-          cclock--;
-          unlock_cclock();
-          for (z = 0; z < MENUBUTTONS; z++)
-            {
-              if (button[z]->isMouseOver ())
-                {
-                  if (ban > -1 && ban != z)
-                    {
-                      button[z]->draw (env->db);
-                      //env->make_update (button[ban]->location.x, button[ban]->location.y, button[ban]->location.w, button[ban]->location.h);
-                    }
-                  ban = z;
-                  break;
-                }
-            }
-          if (!lb && mouse_b & 1)
-            env->mouseclock = 0;
-          lb = (mouse_b & 1) ? 1 : 0;
-          if (mouse_b & 1)
-            {
-              global->wr_lock_command();
-              for (z = 0; z < MENUBUTTONS; z++)
-                {
-                  if (button[z]->isPressed ())
-                    {
-                      if (z == 0)
-                        global->command = GLOBAL_COMMAND_PLAY;
-                      else if (z == 1)
-                        global->command = GLOBAL_COMMAND_HELP;
-                      else if (z == 2)
-                        global->command = GLOBAL_COMMAND_OPTIONS;
-                      else if (z == 3)
-                        global->command = GLOBAL_COMMAND_PLAYERS;
-                      else if (z == 4)
-                        global->command = GLOBAL_COMMAND_CREDITS;
-                      else if (z == 5)
-                        global->command = GLOBAL_COMMAND_NETWORK;
-                      else if (z == 6)
-                        global->command = GLOBAL_COMMAND_QUIT;
-                      done = 1;
-                    }
-                }
-              global->unlock_command();
-            }
-          env->mouseclock++;
-          if (env->mouseclock > 10)
-            env->mouseclock = 0;
-        }
-      if (updaterounds)
-        {
-          updaterounds = 0;
-          env->make_update (global->halfWidth + 27, global->halfHeight + 198, 32, 32);
-        }
-      if (! global->os_mouse) show_mouse (NULL);
-      if (fi)
-        {
-          draw_circlesBG (global, env->db, 0, 0, global->screenWidth, global->screenHeight, true);
-          //textout (env->db, font, (char *)"Rounds: ", global->halfWidth - 45, global->halfHeight + 200, BLACK);
-          //draw_sprite_v_flip (env->db, (BITMAP *) global->gfxData.M[6].dat, global->halfWidth - 60, global->halfHeight + 199);
-          //draw_sprite (env->db, (BITMAP *) global->gfxData.M[6].dat, global->halfWidth + 64, global->halfHeight + 199);
-          for (z = 0; z < MENUBUTTONS; z++)
-           {
-              button[z]->draw (env->db);
-              if (z == current_index)
-              {
-                // int delta_width = (global->language == LANGUAGE_RUSSIAN) ? 20 : 0;
-                // if (global->language == LANGUAGE_GERMAN) delta_width = 20;
-                // draw rectangle around selected option
-                rect(env->db, global->halfWidth - move_text - 8, 
-                     global->halfHeight - 240 + (50 * current_index) + shift_menu,
-                     global->halfWidth + move_text + 8, // - delta_width, 
-                     global->halfHeight - 190 + (50 * current_index) + shift_menu, YELLOW);
-              }
-           }
-        }
-      if (ban > -1)
-        {
-          button[ban]->draw (env->db);
-          //env->make_update (button[ban]->location.x, button[ban]->location.y, button[ban]->location.w, button[ban]->location.h);
-        }
-      //rectfill (env->db, global->halfWidth + 27, global->halfHeight + 198, global->halfWidth + 59, global->halfHeight + 210, WHITE);
-      //rect (env->db, global->halfWidth + 27, global->halfHeight + 198, global->halfWidth + 59, global->halfHeight + 210, BLACK);
-      //textprintf_centre (env->db, font, global->halfWidth + 43, global->halfHeight + 200, BLACK, (char *)"%d", global->rounds);
-      if (! global->os_mouse) show_mouse (env->db);
-      env->make_update (mouse_x, mouse_y, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      env->make_update (lx, ly, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      lx = mouse_x;
-      ly = mouse_y;
-
-      // check for key press
-      if ( keypressed() )
-      {
-          my_key = readkey();
-          my_key = my_key >> 8;
-          fi = 2;
-      }
-      
-      if ( ( my_key == KEY_DOWN ) || (my_key == KEY_S) )
-      {
-         current_index++;
-         if (current_index >= max_index)
-            current_index = 0;
-      }
-      else if ( (my_key == KEY_UP) || (my_key == KEY_W) )
-      {
-         current_index--;
-         if (current_index < 0)
-           current_index = max_index - 1;
-      }
-      else if ( (my_key == KEY_ENTER) || 
-                (my_key == KEY_ENTER_PAD) ||
-                (my_key == KEY_SPACE) )
-      {
-               done = 1;
-               global->wr_lock_command();
-               if (current_index == 0)
-                      global->command = GLOBAL_COMMAND_PLAY;
-               else if (current_index == 1)
-                        global->command = GLOBAL_COMMAND_HELP;
-               else if (current_index == 2)
-                        global->command = GLOBAL_COMMAND_OPTIONS;
-               else if (current_index == 3)
-                        global->command = GLOBAL_COMMAND_PLAYERS;
-               else if (current_index == 4)
-                        global->command = GLOBAL_COMMAND_CREDITS;
-               else if (current_index == 5)
-                        global->command = GLOBAL_COMMAND_NETWORK;
-               else if (current_index == 6)
-                        global->command = GLOBAL_COMMAND_QUIT;
-               global->unlock_command();
-
-      } 
-      else if ( (my_key == KEY_Q) || (my_key == KEY_ESC))
-      {
-          return SIG_QUIT_GAME;
-      }
-      my_key = 0;
-
-      if (fi > 0)
-        {
-          fi--;
-          change (global, env->db);
-        }
-      if (global->close_button_pressed) return SIG_QUIT_GAME;
-      if ( (global->update_string) && (global->update_string[0]) )
-      {  
-       textout_centre_ex (env->db, font, global->update_string, 
-                          global->halfWidth - 20, 500, WHITE, -1);
-       env->make_update(50, 450, 300, 50);
-      }
-      if (global->client_message)
-      {
-        textout_centre_ex(env->db, font, global->client_message,
-                          global->halfWidth - 20, 520, WHITE, -1);
-        env->make_update(50, 450, 300, 100);
-      }
-      env->do_updates ();
-    }
-  clear_keybuf ();
-  return SIG_OK;
-}
-
-void draw_text_in_box (ENVIRONMENT *env, BOX *region, char *text)
-{
-  char buffer[1024];
-  unsigned int lineBegin;
-  int lastSpace = 0;
-  int lineCount;
-  int charCount;
-  int buffCount ;
-
-  rectfill (env->db, region->x, region->y, region->w, region->h,
-            makecol (0,0,128));
-  rect     (env->db, region->x, region->y, region->w, region->h,
-            makecol (128,128,255));
-
-  lineBegin = 0;
-  lineCount = 0;
-  while (lineBegin < strlen (text))
-    {
-      charCount = 0;
-      buffCount = 0;
-      do
-        {
-          buffer[buffCount] = text[lineBegin + charCount];
-          buffer[buffCount+1] = 0;
-          if (buffer[buffCount] == ' ')
-            {
-              lastSpace = 0;
-            }
-          else if (buffer[buffCount] == '\n')
-            {
-              lineCount++;
-              charCount++;
-              break;
-            }
-          lastSpace++;
-          buffCount++;
-          charCount++;
-        }
-      while (text[lineBegin + charCount] && (text_length (font, buffer) < region->w - 20));
-      if ((lastSpace > 0) && (text_length (font, buffer) >= region->w - 20))
-        {
-          charCount -= lastSpace - 1;
-          buffer[buffCount - lastSpace] = 0;
-        }
-      else
-        {
-          buffer[buffCount] = 0;
-        }
-      textout_ex (env->db, font, buffer, region->x + 5,
-                  region->y + (lineCount * text_height (font)) + 5, WHITE, -1);
-      lineBegin = lineBegin + charCount;
-      charCount = 0;
-      lineCount++;
-    }
-  env->make_update (region->x, region->y, region->w, region->h);
-}
+	if (status) {
+		fprintf(stderr, "Unable to start Allegro.\nStatus %d", status);
+		exit(1);
+	}
 
-void draw_buystuff(GLOBALDATA *global, ENVIRONMENT *env, PLAYER *pl)
-{
-  int z;
-  env->make_update (0, 0, global->screenWidth, global->screenHeight);
-  if (! global->os_mouse) show_mouse (NULL);
-
-  draw_circlesBG (global, env->db, 0, 0, global->screenWidth, global->screenHeight, false);
-  draw_sprite (env->db, (BITMAP *) global->misc[DONE_IMAGE], global->halfWidth - 100, global->screenHeight - 50);
-  draw_sprite (env->db, (BITMAP *) global->misc[FAST_UP_ARROW_IMAGE], global->screenWidth - STUFF_BAR_WIDTH - 30, global->halfHeight - 50);
-  draw_sprite (env->db, (BITMAP *) global->misc[UP_ARROW_IMAGE], global->screenWidth - STUFF_BAR_WIDTH - 30, global->halfHeight - 25);
-  draw_sprite (env->db, (BITMAP *) global->misc[DOWN_ARROW_IMAGE], global->screenWidth - STUFF_BAR_WIDTH - 30, global->halfHeight);
-  draw_sprite (env->db, (BITMAP *) global->misc[FAST_DOWN_ARROW_IMAGE], global->screenWidth - STUFF_BAR_WIDTH - 30, global->halfHeight + 25);
-  drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-  env->current_drawing_mode = DRAW_MODE_TRANS;
-
-  for (z = 0; z < global->halfWidth - 200; z++)
-    {
-      set_trans_blender (0, 0, 0, (int)((double)((double)z / (global->halfWidth - 200)) * 240) + 15);
-      vline (env->db, z, 0, 29, pl->color);
-    }
+	// Be sure no vsync is used:
+	const char* no_vsync = get_config_string("graphics", "disable_vsync", "no");
+	if ( strcasecmp("yes", no_vsync) )
+		set_config_string("graphics", "disable_vsync", "yes");
 
-  for (z = 0; z < global->halfWidth - 200; z++)
-    {
-      set_trans_blender (0, 0, 0, (int)((double)((double)z / (global->halfWidth - 200)) * 240) + 15);
-      vline (env->db, (global->screenWidth-1) - z, 0, 29, pl->color);
-    }
+	set_window_title( "Atomic Tanks V" VERSION);
 
-  solid_mode ();
-  env->current_drawing_mode = DRAW_MODE_SOLID;
-  textout_ex (env->db, font, global->ingame->complete_text[14], 20, 420, WHITE, -1);
-  textout_ex(env->db, font, global->ingame->complete_text[15], 20, 450, WHITE, -1);
-  textout_ex(env->db, font, global->ingame->complete_text[16], 20, 465, WHITE, -1);
+	// Before we get started make sure, that if we are using full
+	// screen mode, we have to ignore width and height settings.
+	if (env.full_screen == FULL_SCREEN_TRUE) {
+		status = get_desktop_resolution(&env.screenWidth, &env.screenHeight);
+		if (status < 0) {
+			env.screenWidth = 800;
+			env.screenHeight = 600;
+		}
+		screen_mode = GFX_AUTODETECT_FULLSCREEN;
+	}
 
-}
+	// check for X pressed on the window bar
+	LOCK_FUNCTION(close_button_handler);
+	set_close_button_callback(close_button_handler);
 
-int btps;
-void draw_weapon_list(GLOBALDATA *global, ENVIRONMENT *env, PLAYER *pl, int *trolley, int scroll, int pressed)
-{
-  int slot, zzz;
-  double tempbtps = (global->screenHeight - 55) / STUFF_BAR_HEIGHT;
-  int font_diff;
-
-  (font == global->unicode) ? font_diff = 8 : font_diff = 0;
-
-  // To be sure it rounds down
-  btps = (int)tempbtps;
-  if (tempbtps < btps)
-    btps--;
-
-
-  // go through all items and draw them on the screen with
-  // the amount of items in the trolly
-  for (slot = 1, zzz = scroll; (slot < btps) && (zzz < env->numAvailable); zzz++)
-    {
-      int itemNum = env->availableItems[zzz];
-      draw_sprite (env->db, (BITMAP *)global->gfxData.stuff_bar[(pressed == itemNum)?1:0], global->screenWidth - STUFF_BAR_WIDTH, slot * STUFF_BAR_HEIGHT);
-
-      draw_sprite(env->db, (BITMAP *) global->gfxData.stuff_icon_base, global->screenWidth - STUFF_BAR_WIDTH, (slot * STUFF_BAR_HEIGHT));
-      draw_sprite(env->db, (BITMAP *) global->stock[itemNum], global->screenWidth - STUFF_BAR_WIDTH, (slot * STUFF_BAR_HEIGHT) - 5);
-      env->make_update (global->screenWidth - STUFF_BAR_WIDTH, slot * STUFF_BAR_HEIGHT, STUFF_BAR_WIDTH, STUFF_BAR_HEIGHT + 5);
-
-      if (itemNum >= WEAPONS)  	/* Items part */
-        {
-
-          textout_ex (env->db, font,
-                      item[itemNum - WEAPONS].name, global->screenWidth - STUFF_BAR_WIDTH + 45, (slot * STUFF_BAR_HEIGHT) + 5 - font_diff, BLACK, -1);
-          // Amount in inventory
-          textprintf_ex (env->db, font, global->screenWidth - STUFF_BAR_WIDTH + 45, (slot * STUFF_BAR_HEIGHT) + (STUFF_BAR_HEIGHT/2) - font_diff, BLACK, -1, "%s: %d", global->ingame->complete_text[40], pl->ni[itemNum - WEAPONS]);
-          // Anything in the trolley
-          if (trolley[itemNum] != 0)
-            {
-              int textCol;
-              if (trolley[itemNum] > 0)
-                textCol = makecol (255,255,0);
-              else
-                textCol = makecol (176,0,0);
-              textprintf_ex (env->db, font, global->screenWidth - STUFF_BAR_WIDTH + 45 + text_length (font, "Qty. in inventory: ddd"), (slot * STUFF_BAR_HEIGHT) + (STUFF_BAR_HEIGHT/2) - font_diff, textCol, -1, "%+d", trolley[itemNum]);
-            }
-          sprintf (buf, "$%s", Add_Comma( item[itemNum - WEAPONS].cost ) );
-          textout_ex (env->db, font, buf,
-                      global->screenWidth - 45 - text_length (font, buf), (slot * STUFF_BAR_HEIGHT) + 5 - font_diff, BLACK, -1);
-          sprintf (buf, "for %d", item[itemNum - WEAPONS].amt);
-          textout_ex (env->db, font, buf,
-                      global->screenWidth - 45 - text_length (font, buf), (slot * STUFF_BAR_HEIGHT) + (STUFF_BAR_HEIGHT/2) - font_diff, BLACK, -1);
-        }
-      else  			/* Weapons part */
-        {
-
-          textout_ex (env->db, font, weapon[itemNum].name, global->screenWidth - STUFF_BAR_WIDTH + 45, (slot * STUFF_BAR_HEIGHT) + 5 - font_diff, BLACK, -1);
-          textprintf_ex (env->db, font, global->screenWidth - STUFF_BAR_WIDTH + 45, (slot * STUFF_BAR_HEIGHT) + (STUFF_BAR_HEIGHT/2) - font_diff, BLACK, -1, "%s: %d", global->ingame->complete_text[40], pl->nm[itemNum]);
-          // Anything in the trolley
-          if (trolley[itemNum] != 0)
-            {
-              int textCol;
-              if (trolley[itemNum] > 0)
-                textCol = makecol (255,255,0);
-              else
-                textCol = makecol (176,0,0);
-              textprintf_ex (env->db, font, global->screenWidth - STUFF_BAR_WIDTH + 45 + text_length (font, "Qty. in inventory: ddd"), (slot * STUFF_BAR_HEIGHT) + (STUFF_BAR_HEIGHT/2) - font_diff, textCol, -1, "%+d", weapon[itemNum].delay ? trolley[itemNum] / weapon[itemNum].delay : trolley[itemNum]);
-            }
-          sprintf (buf, "$%s", Add_Comma( weapon[itemNum].cost ));
-          textout_ex (env->db, font, buf,
-                      global->screenWidth - 45 - text_length (font, buf), (slot * STUFF_BAR_HEIGHT) + 5 - font_diff, BLACK, -1);
-          sprintf (buf, "for %d", weapon[itemNum].amt);
-          textout_ex (env->db, font, buf, global->screenWidth - 45 - text_length (font, buf), (slot * STUFF_BAR_HEIGHT) + (STUFF_BAR_HEIGHT/2) - font_diff, BLACK, -1);
-        }
-      slot++;
-    }
+	// Ensure sane colour depth
+	if (! env.colourDepth)
+		env.colourDepth = desktop_color_depth();
 
+	if ( (env.colourDepth != 16) && (env.colourDepth != 32) )
+		env.colourDepth = 16;
+	set_color_depth (env.colourDepth);
 
-}
+	// Now the screen mode can be set
+	if (set_gfx_mode(screen_mode, env.screenWidth, env.screenHeight, 0, 0) < 0) {
+		perror( "set_gfx_mode");
 
-bool buystuff (GLOBALDATA *global, ENVIRONMENT *env)
-{
-  int pl, done; 
-  int updatename, pressed, scroll, lb, lastMouse_b;
-  int hoverOver = 0, z, zz, zzz;
-  char buf[50];
-  int mouse_wheel_previous, mouse_wheel_current;
-  int performed_save_game = FALSE;
-  char description[1024];
-  BOX area = {20, 60, 300, 400};
-  int my_cclock;
-  int item_index = 1;
-  int my_key = 0;
-  int cost, amt, inInv;
-  int buy_count = 0;
-
-  strcpy(description, " ");
-  //avoid compiler warning via initalize
-  lastMouse_b = 0;
-
-  // check starting position of the mouse wheel
-  mouse_wheel_previous = mouse_z;
-
-  lock_cclock();
-  global->updateCount = cclock = lb = env->mouseclock = 0;
-  unlock_cclock();
-  fi = global->stopwindow = updatename = scroll = 1;
-
-  // before we do anything else, put a cap on money
-  for (z = 0; z < global->numPlayers; z++)
-  {
-     if (global->players[z]->money > 1000000000)
-            global->players[z]->money = 1000000000;
-     if (global->players[z]->money < 0)
-            global->players[z]->money = 0;
-  }
-
- 
-  if (global->bIsGameLoaded)
-    // after the first shopping loop the game isn't fresh anymore
-#ifdef DEBUG
-    {
-      cout << endl << "===========================================" << endl;
-      cout << "==          New or Loaded Game!          ==" << endl;
-      cout << "===========================================" << endl << endl;
-#endif // DEBUG
-      global->bIsGameLoaded = false;
-#ifdef DEBUG
-    }
-#endif // DEBUG
-  else if (global->divide_money)
-    {
-      // This only applies if this is not a fresh/loaded game
-      // And if players want to divide the money
-      int iJediMoney = 0;
-      int iJediCount = 0;
-      int iSithMoney = 0;
-      int iSithCount = 0;
-      int iTeamFee	 = 0;
-      
-      for (z = 0; z < global->numPlayers; z++)
-        {
-          /*
-          if (global->players[z]->money > 1000000000)
-            global->players[z]->money = 1000000000;
-          if (global->players[z]->money < 0)
-            global->players[z]->money = 0;
-          */
-          // Sum up team money:
-          if (global->players[z]->team == TEAM_JEDI)
-            {
-              iTeamFee = (int)((double)global->players[z]->money * 0.25);
-              if (iTeamFee > MAX_TEAM_AMOUNT)
-                iTeamFee	=	MAX_TEAM_AMOUNT;
-              iJediMoney += iTeamFee;
-              global->players[z]->money -= iTeamFee;
-              iJediCount++;
-            }
-          if (global->players[z]->team == TEAM_SITH)
-            {
-              iTeamFee = (int)((double)global->players[z]->money * 0.25);
-              if (iTeamFee > MAX_TEAM_AMOUNT)
-                iTeamFee	=	MAX_TEAM_AMOUNT;
-              iSithMoney += iTeamFee;
-              global->players[z]->money -= iTeamFee;
-              iSithCount++;
-            }
-        }
-#ifdef DEBUG
-      cout << endl << "Jedi Count: " << iJediCount << " - SitH Count: " << iSithCount << endl;
-#endif // DEBUG
-      // Now apply the team money:
-      if (iJediCount || iSithCount)
-        {
-#ifdef DEBUG
-          if (iJediCount)
-            printf( (char *)"The Jedi summed up a pool of %13d bucks!\n", iJediMoney);
-          if (iSithCount)
-            printf( (char *)"The Sith summed up a pool of %13d bucks!\n", iSithMoney);
-#endif // DEBUG
-          if (iJediCount)
-            iJediMoney = (int)(((double)iJediMoney * 0.90) / (double)iJediCount);
-          if (iSithCount)
-            iSithMoney = (int)(((double)iSithMoney * 0.90) / (double)iSithCount);
-#ifdef DEBUG
-          if (iJediCount)
-            printf( (char *)"Every Jedi will receive %10d credits out of the pool!\n", iJediMoney);
-          if (iSithCount)
-            printf( (char *)"Every Sith will receive %10d credits out of the pool!\n", iSithMoney);
-#endif // DEBUG
-          for (z = 0; z < global->numPlayers; z++)
-            {
-              if (global->players[z]->team == TEAM_JEDI)
-                global->players[z]->money	+=	iJediMoney;
-              if (global->players[z]->team == TEAM_SITH)
-                global->players[z]->money	+=	iSithMoney;
-            }
-        }
-    }
+		status = set_gfx_mode(screen_mode, 800, 600, 0, 0);
 
-  env->generateAvailableItemsList ();
-  int iMaxBoost = 0;
-  int iMaxScore = 0; 
-  for (pl = 0; pl < global->numPlayers; pl++)
-    {
-      int iCurrentBoostLevel =	global->players[pl]->getBoostValue();
-      if (iCurrentBoostLevel > iMaxBoost)
-        iMaxBoost	=	iCurrentBoostLevel;
-      if (global->players[pl]->score > iMaxScore)
-        iMaxScore = global->players[pl]->score;
+		if ( status < 0 )
+			exit(1);
+		env.screenWidth = 800;
+		env.screenHeight = 600;
     }
+	enable_triple_buffer();
 
-  for (pl = 0; pl < global->numPlayers; pl++)
-    {
-      int money = global->players[pl]->money;
-      int trolley[THINGS];
-      memset (trolley, 0, sizeof (int) * THINGS);
-
-      //have computer players buy stuff
-      if ( (int) global->players[pl]->type != HUMAN_PLAYER)
-        {
-          int pressed = 0;
-          int moneyToSave = 0;	// How much money will the player save?
-          int numDmgWeaps = 0;	// How many damage dealing weapons apart from small missiles do they have=
-          int numPara			=	0;	// how many parachutes do we have?
-          int numProj			=	0;	// How many slick/dimpled projectiles do we have?
-#ifdef DEBUG
-          cout << "(" << global->players[pl]->getName() << ") Starting to buy:" << endl;
-          if (global->players[pl]->defensive < -0.9) cout << "(True Offensive)" << endl;
-          if ((global->players[pl]->defensive >=-0.9) && (global->players[pl]->defensive < -0.75)) cout << "(Very Offensive)" << endl;
-          if ((global->players[pl]->defensive >=-0.75) && (global->players[pl]->defensive < -0.25)) cout << "(Offensive)" << endl;
-          if ((global->players[pl]->defensive >=-0.25) && (global->players[pl]->defensive < 0.00)) cout << "(Slightly Offensive)" << endl;
-          if (global->players[pl]->defensive == 0.0)	cout << "(Neutral)" << endl;
-          if ((global->players[pl]->defensive >0.0) && (global->players[pl]->defensive <= 0.25)) cout << "(Slightly Defensive)"<< endl;
-          if ((global->players[pl]->defensive >0.25) && (global->players[pl]->defensive <= 0.75)) cout << "(Defensive)" << endl;
-          if ((global->players[pl]->defensive >0.75) && (global->players[pl]->defensive <= 0.9)) cout << "(Very Defensive)" << endl;
-          if (global->players[pl]->defensive > 0.9)	cout << "(True Defensive)" << endl;
-          cout << "Inventory:" << endl <<  "----------" << endl;
-          for (int i = 1; i < WEAPONS; i++)
-            {
-              if (global->players[pl]->nm[i])
-                cout << global->players[pl]->nm[i] << " x " << weapon[i].name << endl;
-            }
-          cout << " - - - - - - -" << endl;
-          for (int i = 1; i < ITEMS; i++)
-            {
-              if (global->players[pl]->ni[i])
-                cout << global->players[pl]->ni[i] << " x " << item[i].name << endl;
-            }
-          cout << "----------" << endl;
-#endif // DEBUG
-          // moneysaving will be made possible when:
-          // 1. It's not the first three rounds
-          // 2. It's not the last 5 rounds
-          // and, dynamically:
-          // 3. We have at least 10 parachutes  or no gravity
-          // 4. We have at least 5 damage dealing (not small missile) weapons
-          // 5. We have a sum of 50 slick/dimpled projectiles
-
-          if	( (global->currentround > 5)
-               && ( ( (int) global->rounds - global->currentround) > 3))
-            {
-              moneyToSave	=	global->players[pl]->getMoneyToSave();
-#ifdef DEBUG
-              cout << "Maximum Money to save: " << moneyToSave << " (I have: " << global->players[pl]->money << ")" << endl;
-#endif //DEBUG
-            }
-#ifdef DEBUG
-          else
-            cout << "No money to save this round!!!" << endl;
-#endif // DEBUG
-          // Check for a minimum of damage dealing weapons and parachutes, then buy until moneyToSave is reached
-          buy_count = 0;
-          do
-            {
-              numPara			=	global->players[pl]->ni[ITEM_PARACHUTE];
-              numProj			= global->players[pl]->ni[ITEM_SLICKP] + global->players[pl]->ni[ITEM_DIMPLEP];
-              numDmgWeaps	=	0;
-
-              for (int i = 1; i < WEAPONS; i++)
-                {
-                  // start from 1, as 0 is the small missile
-                  if (weapon[i].damage > 0)
-                    numDmgWeaps += global->players[pl]->nm[i];
-                }
-
-              if	( (global->players[pl]->money > moneyToSave)
-                   || ( (numPara			< 10) && (env->landSlideType > LANDSLIDE_NONE))
-                   || (numDmgWeaps	<	8)
-                   || (numProj			< 50))
-               {
-                pressed = global->players[pl]->chooseItemToBuy (iMaxBoost);
-               }
-              else
-                pressed	=	-1;
-
-#ifdef DEBUG
-              if (pressed > -1)
-                {
-                  cout << "I have bought: ";
-                  if (pressed < WEAPONS)
-                    cout << weapon[pressed].name;
-                  else
-                    cout << item[pressed - WEAPONS].name;
-                  cout << " (" << global->players[pl]->money << " bucks left)" << endl;
-                }
-              else
-                cout << "Finished, with " << global->players[pl]->money << " Credits left!" << endl;
-#endif // DEBUG
-            buy_count++;
-            }
-          while ( (pressed != -1) && (buy_count < 100) );
-
-#ifdef DEBUG
-          cout << "============================================" << endl << endl;
-#endif //DEBUG
-          continue;  //go to next player
-        }        // end of AI player
-
-      done = 0;
-      updatename = scroll = 1;
-      pressed = -1;
-
-      draw_buystuff (global, env, global->players[pl]);
-
-      while (!done)
-        {
-
-          LINUX_SLEEP;
-          my_cclock = CLOCK_MAX;
-          while (my_cclock > 0)
-            {
-              LINUX_SLEEP;
-              if (global->close_button_pressed)
-                {
-                  clear_keybuf();
-                  return false;
-                }
-              my_cclock--;
-              if (!lb && mouse_b & 1 && mouse_x >= global->halfWidth - 100 && mouse_x < global->halfWidth + 100 && mouse_y >= global->screenHeight - 50 && mouse_y < global->screenHeight - 25)
-                done = 1;
-              if (!lb && mouse_b & 1)
-                env->mouseclock = 0;
-              lb = (mouse_b & 1) ? 1 : 0;
-
-              my_key = 0;
-              //Keyboard control
-              if ( keypressed() )
-              {
-                 my_key = readkey();
-                 my_key = my_key >> 8;
-              }
-
-              if  ( my_key == KEY_UP || my_key == KEY_W) // && (scroll > 1) 
-                //  && (!env->mouseclock))
-                {
-                  if (item_index > 1)
-                     item_index--;
-                  if (scroll > 1)
-                     scroll--;
-                }
-              if (my_key == KEY_PGUP || my_key == KEY_R) // && (scroll > 1)
-                  // && (!env->mouseclock))
-                {
-                  item_index -= btps / 2;
-                  if (item_index < 1)
-                     item_index = 1;
-                  scroll -= btps / 2;
-                  if (scroll < 1)
-                    scroll = 1;
-                }
-
-              if (my_key == KEY_DOWN || my_key == KEY_S)
-              //    && (scroll <= env->numAvailable - btps)
-              //    && (!env->mouseclock))
-                {
-                  if (item_index < (env->numAvailable - 1))
-                     item_index++;
-                 if (scroll <= env->numAvailable - btps)
-                     scroll++;
-                }
-              if ((my_key == KEY_PGDN || my_key == KEY_F)
-                  && (scroll <= env->numAvailable - btps + 1) )
-                //  && (!env->mouseclock))
-                {
-                  item_index += btps / 2;
-                  if (item_index > env->numAvailable - btps)
-                     item_index = env->numAvailable - btps;
-                  scroll += btps / 2;
-                  if (scroll > env->numAvailable - btps + 1)
-                    scroll = env->numAvailable - btps + 1;
-                }
-
-              // make sure the selected item is on the visible screen
-              if (item_index < scroll)
-                 item_index = scroll;
-              else if ( item_index > (scroll + btps + 3) )
-                 item_index = scroll + btps + 3;
-
-              // buy or sell an item
-              if (my_key == KEY_RIGHT || my_key == KEY_D) 
-              {
-                    pressed = env->availableItems[item_index];
-                    if (pressed >= WEAPONS)
-                    {
-                      cost = item[pressed - WEAPONS].cost;
-                      amt = item[pressed - WEAPONS].amt;
-                      inInv = global->players[pl]->ni[pressed - WEAPONS];
-                    }
-                  else
-                    {
-                      cost = weapon[pressed].cost;
-                      amt = weapon[pressed].amt;
-                      inInv = global->players[pl]->nm[pressed];
-                    }
-                     if ((money >= cost)
-                          && ( (inInv + trolley[pressed]) < (999 - amt)))
-                        {
-                          if (trolley[pressed] <= -amt)
-                            {
-                              if (global->sellpercent > 0.01)
-                                {
-                                  money -= (int)(cost * global->sellpercent);
-                                  trolley[pressed] += amt;
-                                  updatename = 1;
-                                }
-                            }
-                          else
-                            {
-                              money -= cost;
-                              trolley[pressed] += amt;
-                              updatename = 1;
-                              if (inInv + trolley[pressed] > 999)
-                                trolley[pressed] = 999;
-                            }
-                        }
-                   pressed = -1;
-              }         // end of buying
-
-              if ( my_key == KEY_LEFT || my_key == KEY_A)
-              {
-                  pressed = env->availableItems[item_index];
-                    if (pressed >= WEAPONS)
-                    {
-                      cost = item[pressed - WEAPONS].cost;
-                      amt = item[pressed - WEAPONS].amt;
-                      inInv = global->players[pl]->ni[pressed - WEAPONS];
-                    }
-                  else
-                    {
-                      cost = weapon[pressed].cost;
-                      amt = weapon[pressed].amt;
-                      inInv = global->players[pl]->nm[pressed];
-                    }
-                    if (inInv + trolley[pressed] >= amt)
-                    {
-                          if (trolley[pressed] >= amt)
-                          {
-                              money += cost;
-                              trolley[pressed] -= amt;
-                              updatename = 1;
-                          }
-                          else
-                          {
-                              if (global->sellpercent > 0.01)
-                                {
-                                  money += (int)(cost * global->sellpercent);
-                                  trolley[pressed] -= amt;
-                                  updatename = 1;
-                                }
-                          }
-                     }
-                   pressed = -1;
-              }           // end of selling
-
-
-              // check for adding or removing rounds
-              if ( (my_key == KEY_PLUS_PAD) || (my_key == KEY_EQUALS) )
-                {
-                  if ( (global->rounds < 999) && (! env->mouseclock) )
-                    {
-                      global->rounds++;
-                      global->currentround++;
-                      updatename = 1;
-                    }
-                }
-              if ( (my_key == KEY_MINUS_PAD) || (my_key == KEY_MINUS) )
-                {
-                  if ( (global->rounds > 1) && (global->currentround > 1)
-                       && (! env->mouseclock) )
-                    {
-                      global->rounds--;
-                      global->currentround--;
-                      updatename = 1;
-                    }
-                }
-
-              // check for saving the game
-              if ( my_key == KEY_F10 )
-                {
-                  int status;
-                  if (! performed_save_game)
-                    {
-                      status = Save_Game(global, env);
-                      performed_save_game = TRUE;
-                      if (status)
-                        snprintf(description, 64, "%s \"%s\".", global->ingame->complete_text[17], global->game_name);
-                      else
-                        strcpy(description, global->ingame->complete_text[41]);
-                      draw_text_in_box (env, &area, description);
-                    }
-                }
-
-              if ( my_key == KEY_ENTER)
-                 done = TRUE;
-
-              // Mouse control
-
-              // check mouse wheel
-              mouse_wheel_current = mouse_z;
-              if (mouse_wheel_current < mouse_wheel_previous)
-                {
-                  scroll++;
-                  if (scroll > env->numAvailable - btps + 1)
-                    scroll = env->numAvailable - btps + 1;
-                  if (scroll > item_index)
-                     item_index = scroll;
-                }
-              else if (mouse_wheel_current > mouse_wheel_previous)
-                {
-                  scroll--;
-                  if (scroll < 1)
-                    scroll = 1;
-                  if (item_index > scroll + btps)
-                     item_index = scroll + btps - 3;
-                }
-              mouse_wheel_previous = mouse_wheel_current;
-
-
-              if (mouse_x >= global->screenWidth - STUFF_BAR_WIDTH && mouse_x < global->screenWidth)
-                {
-                  int newlyOver;
-                  zz = 0;
-                  for (z = 1, zzz = scroll; z < btps; z++, zzz++)
-                    {
-                      if (mouse_y >= z * STUFF_BAR_HEIGHT && mouse_y < (z * STUFF_BAR_HEIGHT) + 30)
-                        {
-                          zz = 1;
-                          break;
-                        }
-                    }
-                  if (zz)
-                    newlyOver = env->availableItems[zzz];
-                  else
-                    newlyOver = -1;
-                  if (hoverOver != newlyOver)
-                    {
-                      // char description[1024];
-                      // BOX area = {20, 60, 300, 400};
-
-                      if (newlyOver > -1)
-                        {
-                          if (newlyOver < WEAPONS)
-                            {
-                              WEAPON *weap = &weapon[newlyOver];
-                              sprintf (description, "Radius: %d\nYield: %ld\n\n%s",
-                                       weap->radius, calcTotalPotentialDamage (newlyOver) * weap->spread, weap->description);
-                            }
-                          else
-                            {
-                              int itemNum = newlyOver - WEAPONS;
-                              ITEM *it = &item[itemNum];
-                              if (itemNum >= ITEM_VENGEANCE && itemNum <= ITEM_FATAL_FURY)
-                                {
-                                  sprintf (description, "Potential Damage: %ld\n\n%s",
-                                           calcTotalPotentialDamage ((int)it->vals[0]) * (int)it->vals[1],
-                                           it->description);
-                                }
-                              else
-                                {
-                                  sprintf (description, "%s", it->description);
-                                }
-                            }
-                        }
-                      else
-                        {
-                          description[0] = 0;
-                        }
-                      hoverOver = newlyOver;
-
-                      draw_text_in_box (env, &area, description);
-                    }
-                }
-              if (mouse_b & 1 && !env->mouseclock)
-                {
-                  int scrollArrowPos = global->screenWidth - STUFF_BAR_WIDTH - 30;
-                  if (mouse_x >= scrollArrowPos && mouse_x < scrollArrowPos + 24)
-                    {
-                      if ((mouse_y >= global->halfHeight - 50 && mouse_y < global->halfHeight - 25) && (scroll > 1))
-                        {
-                          scroll -= btps / 2;
-                          if (scroll < 1)
-                            scroll = 1;
-                        }
-                      if ((mouse_y >= global->halfHeight - 24 && mouse_y < global->halfHeight) && (scroll > 1))
-                        {
-                          scroll--;
-                        }
-                      if ((mouse_y >= global->halfHeight + 1 && mouse_y < global->halfHeight + 25) && (scroll <= env->numAvailable - btps))
-                        {
-                          scroll++;
-                        }
-                      if ((mouse_y >= global->halfHeight + 25 && mouse_y < global->halfHeight + 50) && (scroll <= env->numAvailable - btps + 1))
-                        {
-                          scroll += btps / 2;
-                          if (scroll > env->numAvailable - btps + 1)
-                            scroll = env->numAvailable - btps + 1;
-                        }
-                    }
-                    if (item_index < scroll)
-                       item_index = scroll;
-                    else if ( item_index > (scroll + btps) )
-                       item_index = scroll + btps - 3;
-                }
-              if (mouse_b & 1 || mouse_b & 2)
-                {
-                  int itemButtonClicked = 0;
-                  for (int buttonCount = 1, currItem = scroll; buttonCount < btps; buttonCount++, currItem++)
-                    {
-                      if (mouse_x >= global->screenWidth - STUFF_BAR_WIDTH && mouse_x < global->screenWidth && mouse_y >= buttonCount * STUFF_BAR_HEIGHT && mouse_y < (buttonCount * STUFF_BAR_HEIGHT) + 30)
-                        {
-                          itemButtonClicked = 1;
-                          // Remember which button was pressed
-                          pressed = env->availableItems[currItem];
-                        }
-                    }
-                  if (!itemButtonClicked)
-                    {
-                      pressed = -1;
-                    }
-                }
-              if (pressed > -1 && !(mouse_b & 1 || mouse_b & 2))
-                {
-                  // Cost, amount and in-inventory amount
-                  // of pressed item
-                  // int cost,amt,inInv;
-                  bool control_key = false;
-
-                  if ( ( key[KEY_LCONTROL] ) || ( key[KEY_RCONTROL] ) )
-                    control_key = true;
-
-                  if (pressed > WEAPONS - 1)
-                    {
-                      cost = item[pressed - WEAPONS].cost;
-                      amt = item[pressed - WEAPONS].amt;
-                      inInv = global->players[pl]->ni[pressed - WEAPONS];
-                    }
-                  else
-                    {
-                      cost = weapon[pressed].cost;
-                      amt = weapon[pressed].amt;
-                      inInv = global->players[pl]->nm[pressed];
-                    }
-
-                  if (control_key)
-                    {
-                      cost *= 10;
-                      amt *= 10;
-                    }
-
-                  if (lastMouse_b & 2)
-                    {
-                      if (inInv + trolley[pressed] >= amt)
-                        {
-                          if (trolley[pressed] >= amt)
-                            {
-                              money += cost;
-                              trolley[pressed] -= amt;
-                              updatename = 1;
-                            }
-                          else
-                            {
-                              if (global->sellpercent > 0.01)
-                                {
-                                  money += (int)(cost * global->sellpercent);
-                                  trolley[pressed] -= amt;
-                                  updatename = 1;
-                                }
-                            }
-                        }
-                    }
-                  else
-                    {
-                      if ((money >= cost)
-                          && ( (inInv + trolley[pressed]) < (999 - amt)))
-                        {
-                          if (trolley[pressed] <= -amt)
-                            {
-                              if (global->sellpercent > 0.01)
-                                {
-                                  money -= (int)(cost * global->sellpercent);
-                                  trolley[pressed] += amt;
-                                  updatename = 1;
-                                }
-                            }
-                          else
-                            {
-                              money -= cost;
-                              trolley[pressed] += amt;
-                              updatename = 1;
-                              if (inInv + trolley[pressed] > 999)
-                                trolley[pressed] = 999;
-                            }
-                        }
-                    }
-                  pressed = -1;
-                }
-              env->mouseclock++;
-              if (env->mouseclock > 5)
-                env->mouseclock = 0;
-              lastMouse_b = mouse_b;
-            }
-          if (! global->os_mouse) show_mouse (NULL);
-          if (updatename)
-            {
-              updatename = 0;
-              // env->make_update (global->halfWidth - 315, 0, 400, 30);
-              env->make_update (global->halfWidth - 315, 0, global->screenWidth - 1, 30);
-              draw_sprite (env->db, (BITMAP *) global->gfxData.stuff_bar[0], global->halfWidth - 200, 0);
-              textprintf_ex (env->db, font, global->halfWidth - 190, 5, BLACK, -1, "%s %d: %s", global->ingame->complete_text[10], pl + 1, global->players[pl]->getName ());
-              // textprintf_ex (env->db, font, global->halfWidth - 190, 17, BLACK, -1, "Money: $%d", money);
-              textprintf_ex(env->db, font, global->halfWidth - 190, 17, BLACK, -1, "%s: $%s", global->ingame->complete_text[11], Add_Comma(money));
-              sprintf (buf, "%s: %d/%d", global->ingame->complete_text[12], (int)(global->rounds - global->currentround) + 1, (int)global->rounds);
-              textout_ex (env->db, font, buf, global->halfWidth + 170 - text_length (font, buf), 5, BLACK, -1);
-              sprintf (buf, "%s: %d", global->ingame->complete_text[13], global->players[pl]->score);
-              textout_ex (env->db, font, buf, global->halfWidth + 155 - text_length (font, buf), 17, BLACK, -1);
-            }
-
-          draw_weapon_list(global, env, global->players[pl], trolley, scroll, (pressed < 0) ? env->availableItems[item_index] : pressed );
-          env->make_update (mouse_x, mouse_y, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-          env->make_update (lx, ly, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-          lx = mouse_x;
-          ly = mouse_y;
-          if (! global->os_mouse) show_mouse (env->db);
-          if (fi)
-            {
-              change (global, env->db);
-              fi = 0;
-            }
+	env.halfWidth = env.screenWidth / 2;
+	env.halfHeight = env.screenHeight / 2;
 
-          else
-            env->do_updates ();
-        }
-      for (int tItem = 0; tItem < WEAPONS; tItem++)
-        global->players[pl]->nm[tItem] += trolley[tItem];
-      for (int tItem = WEAPONS; tItem < THINGS; tItem++)
-        global->players[pl]->ni[tItem - WEAPONS] += trolley[tItem];
-      global->players[pl]->money = money;
-    }
-  // clear_keybuf ();
-
-  for (z = 0; z < global->numPlayers; z++)
-    {
-      int iMoney		= global->players[z]->money;
-      int iInterest = 0;
-      float fIntPerc= 0.0;
-      int iLevel		=	0;
-      int iInterSum = 0; // The summed up interest
-#ifdef DEBUG
-      cout << endl << "======================================================" << endl;
-      printf( (char *)"%2d.: %s enters the bank to get interest:\n", (z+1), global->players[z]->getName());
-      printf( (char *)"     Starting Account: %10d\n", global->players[z]->money);
-      cout << "------------------------------------------------------" << endl;
+#ifdef ATANKS_IS_MSVC
+# if defined(ATANKS_DEBUG)
+	SetConsoleCtrlHandler(ctrlHandler, TRUE);
 #endif // DEBUG
-      while (iMoney && (iLevel < 5))
-        {
-          // Enter next level
-          iLevel++;
-          fIntPerc	=	(global->interest - 1.0) / iLevel;
-          iInterest = (int)((float)iMoney * fIntPerc);
-          // The limit is only applicable on the first four levels, in the fifth level interest is fully applied!
-          if ((iInterest > MAX_INTEREST_AMOUNT) && (iLevel < 5))
-            iInterest = MAX_INTEREST_AMOUNT;
-          // Now sum the interest up and substract the counted money!
-          iInterSum	+=	iInterest;
-          iMoney		-=	(int)((float)iInterest / fIntPerc);
-#ifdef DEBUG
-          printf( (char *)"     Level %1d:  %8d credits are rated,\n", iLevel, (int)(iInterest / fIntPerc));
-          printf( (char *)"     Interest: %8d credits. (%5.2f%%)\n", iInterest, (fIntPerc * 100));
-#endif // DEBUG
-          // To get rid of (possible) rounding errors, add a security check:
-          if ((iMoney < (4 * iLevel)) || (iInterest < 1))
-            iMoney = 0; // With less there won't be any more interest anyway!
-#ifdef DEBUG
-          printf( (char *)"     Unrated : %8d credits left.\n", iMoney);
-#endif // DEBUG
-        }
-      // Now giv'em their money:
-#ifdef DEBUG
-      printf( (char *)"     Sum:      %8d credits.\n", iInterSum);
-      cout << "------------------------------------------------------" << endl;
-#endif // DEBUG
-      global->players[z]->money += iInterSum;
-#ifdef DEBUG
-      printf( (char *)"     Final Account   : %10d\n", global->players[z]->money);
-      cout << "======================================================" << endl;
-#endif // DEBUG
-    }
-    return true;
-}
-
+	if ( env.full_screen == FULL_SCREEN_TRUE )
+		set_display_switch_mode(SWITCH_BACKAMNESIA);
+	else
+		set_display_switch_mode(SWITCH_BACKGROUND);
+#endif // ATANKS_IS_WINDOWS
+
+	if (install_keyboard () < 0) {
+		perror ( "install_keyboard failed");
+		exit (1);
+	}
+
+	if (install_mouse () < 0)
+		perror ( "install_mouse failed");
+
+	// check to see if we want sound
+	if (env.sound_enabled) {
+		int32_t sound_type = DIGI_AUTODETECT;
+
+#       ifdef ATANKS_IS_LINUX
+		switch ( env.sound_driver ) {
+			case SD_OSS: sound_type = DIGI_OSS; break;
+			case SD_ESD: sound_type = DIGI_ESD; break;
+			case SD_ARTS: sound_type = DIGI_ARTS; break;
+			case SD_ALSA: sound_type = DIGI_ALSA; break;
+			case SD_JACK: sound_type = DIGI_JACK; break;
+			default: sound_type = DIGI_AUTODETECT; break;
+		}
+#         ifdef UBUNTU
+			if (DIGI_AUTODETECT == sound_type)
+				sound_type = DIGI_OSS;
+#         endif // UBUNTU
+#       endif // ATANKS_IS_LINUX
+
+		int32_t channels = detect_digi_driver(sound_type);
+
+		if (!channels && (DIGI_AUTODETECT != sound_type)) {
+			sound_type = DIGI_AUTODETECT;
+			channels   = detect_digi_driver(sound_type);
+		}
+
+		if (channels) {
+			env.voices = channels >  64 ?  32
+							: channels >  32 ?  16
+							: channels >  16 ?   8
+							: channels;
+
+			int32_t snd_installed = -1;
+
+			while ( (env.voices > 1) && (0 > snd_installed)) {
+				DEBUG_LOG("Sound Init", "Reserving %d / %d voices", env.voices, channels)
+				reserve_voices(env.voices, 0);
+				snd_installed = install_sound(sound_type, DIGI_NONE, nullptr);
+
+				// Instead of failing directly, reduces voices first
+				if (-1 == snd_installed) {
+					DEBUG_LOG("Sound Init", "Too many voices, halving...", 0)
+					env.voices /= 2;
+				}
+			}
+
+			// Now display an error message if it was not possible to succeed
+			if (0 > snd_installed) {
+				fprintf (stderr, "install_sound: failed initialising sound\n");
+				fprintf (stderr, "Please try selecting a different Sound Driver"
+							     " from the Options menu.\n");
+			} else {
+				int32_t set_voices = get_mixer_voices();
+				DEBUG_LOG("Sound Init", "Mixer has %d voices", set_voices)
+
+				if (set_voices < env.voices)
+					env.voices = set_voices;
+
+				// Set the mixer quality:
+				int32_t mixq = get_mixer_quality();
+				if (mixq < 2) {
+					DEBUG_LOG("Sound Init", "Raising mixer quality from %d to 2",
+								mixq)
+					set_mixer_quality(2);
+				}
+
+			}
+		} else
+			fprintf (stderr, "detect_digi_driver detected no sound device\n");
+	} // End of sound initialization
+
+	srand (time (0));
+
+  	// Colour initialization, must be done here when allegro is initialized.
+	BLACK       = makecol (0x00, 0x00, 0x00);
+	BLUE        = makecol (0x00, 0x00, 0xff);
+	DARK_GREEN  = makecol (0x00, 0x50, 0x00);
+	DARK_GREY   = makecol (0x40, 0x40, 0x40);
+	DARK_RED    = makecol (0x80, 0x00, 0x00);
+	GREY        = makecol (0x80, 0x80, 0x80);
+	GREEN       = makecol (0x00, 0xff, 0x00);
+	LIGHT_GREEN = makecol (0x80, 0xff, 0x80);
+	LIME_GREEN  = makecol (0xc8, 0xff, 0xc8);
+	ORANGE      = makecol (0xfa, 0x96, 0x00);
+	PINK        = makecol (0xff, 0x00, 0xff);
+	PURPLE      = makecol (0xc8, 0x00, 0xc8);
+	RED         = makecol (0xff, 0x00, 0x00);
+	SILVER      = makecol (0xc0, 0xc0, 0xc0);
+	TURQUOISE   = makecol (0x96, 0xc8, 0xff);
+	WHITE       = makecol (0xff, 0xff, 0xff);
+	YELLOW      = makecol (0xff, 0xff, 0x00);
+
+	// Start preparing environment
+	env.first_init();    // *MUST* be done before GLOBALDATA or
+	global.first_init(); // max_screen_updates is not correct!
+
+	// Prepare remaining environment
+	clear_to_color (global.canvas, BLACK);
+	env.loadBitmaps();
+	title();
+	env.loadSounds();
+
+	init_mouse_cursor();
+
+	env.loadFonts();
+
+	env.window.x = 0;
+	env.window.y = 0;
+	env.window.w = 0;
+	env.window.h = 0;
+
+	for (int32_t z = 0; z < env.max_screen_updates; z++) {
+		global.updates[z].x = 0;
+		global.updates[z].y = 0;
+		global.updates[z].w = 0;
+		global.updates[z].h = 0;
+	}
 
-
-#ifdef GETV_IS_EVER_USED
-double getv (int color)
-{
-  float h, s, v;
-  int r, g, b;
-
-  r = getr (color);
-  g = getg (color);
-  b = getb (color);
-  rgb_to_hsv (r, g, b, &h, &s, &v);
-
-  return (v);
 }
-#endif //GETV_IS_EVER_USED
 
 
-
-void set_level_settings (GLOBALDATA *global, ENVIRONMENT *env)
+static void initialisePlayers()
 {
-  int taken[MAXPLAYERS];
-  BITMAP *sky_gradient_strip, *land_gradient_strip;
-  int chosen = 0, chosenCount = 0, peak_height = 0;
-  int z, zz; 
-  int objCount;
-  TANK *ltank;
-  int xoffset;
-
-  // srand (time (NULL));
-
-  if (! global->os_mouse) show_mouse (NULL);
-  draw_sprite (screen, (BITMAP *) global->misc[1], global->halfWidth - 120, global->halfHeight + 115);
-  textout_centre_ex (screen, font, global->ingame->complete_text[42], global->halfWidth, global->halfHeight + 120, WHITE, -1);
-
-  // Choose appropriate gradients for sky and land
-  while ((chosenCount < 60) && !chosen)
-    {
-      global->lock_curland();
-      global->curland = rand () % LANDS;
-      global->unlock_curland();
-      if (global->colour_theme == COLOUR_THEME_CRISPY)
-      {
-        global->lock_curland();
-        global->curland += LANDS;
-        global->unlock_curland();
-      }
-      global->lock_curland();
-      if (!global->gfxData.land_gradient_strips[global->curland])
-        global->gfxData.land_gradient_strips[global->curland] = create_gradient_strip (land_gradients[global->curland], (global->screenHeight - MENUHEIGHT));
-      land_gradient_strip = global->gfxData.land_gradient_strips[global->curland];
-      global->unlock_curland();
-
-      global->cursky = rand () % SKIES;
-      if (global->colour_theme == COLOUR_THEME_CRISPY)
-        global->cursky += SKIES;
-      if (!global->gfxData.sky_gradient_strips[global->cursky])
-        global->gfxData.sky_gradient_strips[global->cursky] = create_gradient_strip (sky_gradients[global->cursky], (global->screenHeight - MENUHEIGHT));
-      sky_gradient_strip = global->gfxData.sky_gradient_strips[global->cursky];
-
-      chosen = 1;
-      for (z = 0; z < global->screenWidth; z++)
-        if (peak_height < env->height[z])
-          peak_height = (int)env->height[z];
-      for (z = 0; z <= peak_height; z++)
-        {
-          int skyi, landi;
-          double distance;
-          skyi = getpixel (sky_gradient_strip, 0, (global->screenHeight - MENUHEIGHT - z));
-          landi = getpixel (land_gradient_strip, 0, z);
-
-          distance = colorDistance (skyi, landi);
-          if (distance < 30)
-            chosen = 0;
-        }
-      chosenCount++;
-    }
-
-  if (! global->os_mouse) show_mouse (NULL);
-  draw_sprite (screen, (BITMAP *) global->misc[1], global->halfWidth - 120, global->halfHeight + 155);
-  textout_centre_ex (screen, font, global->ingame->complete_text[43], global->halfWidth, global->halfHeight + 160, WHITE, -1);
-  // It looks like we do not use this anymore  xoffset = rand ();
-  //generate_sky (global, env, xoffset, 0, global->screenWidth, global->screenHeight);
- 
-  if (env->sky)
-  {
-     destroy_bitmap(env->sky);
-     env->sky = NULL;
-  } 
-  // see if we want a custom background
-  if ( (env->custom_background) && (env->bitmap_filenames) )
-  {
-     // if ( env->sky) destroy_bitmap(env->sky);
-     env->sky = load_bitmap( env->bitmap_filenames[ rand() % env->number_of_bitmaps ], NULL);
-  }
-
-  // if we do not have a custom background (or do not want one) create a new background
-  if ( (! env->custom_background) || (! env->sky) )
-  {
-      // if ( env->sky ) destroy_bitmap(env->sky);
-#ifdef THREADS
-      // On Linux we will have a thread creating a sky for us in the background
-      // to avoid wait times. If there is not a pre-created sky waiting for us
-      // then fall back to generating one the regular way.
-      if (env->get_waiting_sky())
-      {
-           env->sky = env->get_waiting_sky();
-           env->lock(env->waiting_sky_lock);
-           env->waiting_sky = NULL;
-           env->unlock(env->waiting_sky_lock);
-      }
-      else
-      {
-#endif
-      env->sky = create_bitmap( global->screenWidth, global->screenHeight - MENUHEIGHT);
-      generate_sky (global, env->sky, sky_gradients[global->cursky],
-                   (global->ditherGradients ? GENSKY_DITHERGRAD : 0 ) |
-                   (global->detailedSky ? GENSKY_DETAILED : 0 )  );
-#ifdef THREADS
-      }
-#endif
-  }
-
-  if (! global->os_mouse) show_mouse (NULL);
-  draw_sprite (screen, (BITMAP *) global->misc[1], global->halfWidth - 120, global->halfHeight + 195);
-  textout_centre_ex (screen, font, global->ingame->complete_text[44], global->halfWidth, global->halfHeight + 200, WHITE, -1);
-
-  #ifdef THREADS
-  // we have threads, so check for pre-main terrain
-  if (env->get_waiting_terrain())
-  {
-      // we have one waiting, so destroy the current one
-      if (env->terrain) destroy_bitmap(env->terrain);
-      env->terrain = env->get_waiting_terrain();
-      env->lock(env->waiting_terrain_lock);
-      env->waiting_terrain = NULL;        // set this so a new one will be made
-      env->unlock(env->waiting_terrain_lock);
-  }
-  else      // one is not waiting, so we need to create one now
-  {
-  #endif
-
-  clear_to_color (env->terrain, PINK);
-
-  xoffset = rand ();
-  generate_land (global, env, env->terrain, xoffset, 0, global->screenHeight);
-  #ifdef THREADS
-  }         // end of else
-  #endif
-
-  for (z = 0; z < global->numTanks; z++)
-    {
-      taken[z] = 0;
-    }
-  for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-    {
-      for (zz = rand () % global->numTanks; taken[zz]; zz = rand () % global->numTanks) { }
-      taken[zz] = objCount + 1;
-      ltank->x = (zz + 1) * (global->screenWidth / (global->numTanks + 1));
-      ltank->y = (global->screenHeight - (int)env->height[(int) ltank->x]) - (TANKHEIGHT - TANKSAG);
-      ltank->newRound ();
-    }
-  for (z = 0; z < MAXPLAYERS; z++)
-    env->order[z] = NULL;
-  global->maxNumTanks = global->numTanks;
-  for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-    {
-      for (z = rand () % global->numTanks; env->order[z]; z = rand () % global->numTanks) { }
-      env->order[z] = ltank;
-    }
-  // if (global->turntype != TURN_RANDOM) {
-  if ( (global->turntype != TURN_RANDOM) &&
-       (global->turntype != TURN_SIMUL))
-    {
-      for (int index = 0; index < global->maxNumTanks - 1; index++)
-        {
-          int swap = FALSE;
-          if (global->turntype == TURN_HIGH)
-            {
-              if (env->order[index]->player->score <
-                  env->order[index + 1]->player->score)
-                {
-                  swap = TRUE;
-                }
-            }
-          else if (global->turntype == TURN_LOW)
-            {
-              if (env->order[index]->player->score >
-                  env->order[index + 1]->player->score)
-                {
-                  swap = TRUE;
-                }
-            }
-          if (swap)
-            {
-              TANK *tempTank = env->order[index];
-              env->order[index] =
-                env->order[index + 1];
-              env->order[index + 1] = tempTank;
-              index = -1;
-            }
-        }
-    }
+	for (int32_t z = 0; z < env.numGamePlayers; ++z) {
+		env.players[z]->money = env.startmoney;
+		env.players[z]->score = 0;
+		if ( (HUMAN_PLAYER != env.players[z]->type)
+		  && (PERPLAY_PREF == env.players[z]->preftype))
+			env.players[z]->generatePreferences();
+		env.players[z]->initialise(false);
+		env.players[z]->type_saved = env.players[z]->type;
+	}
 }
 
 
-char *do_winner (GLOBALDATA *global, ENVIRONMENT *env)
+static bool loadConfig()
 {
-  int maxscore = -1;
-  int winindex = -1;
-  int i, index, *order;
-  bool multiwinner = false;
-  int fonthgt = text_height(font)+10;
-  int jedi_index = -1, sith_index = -1, neutral_index = -1;
-  // char *my_quote;
-  char *return_string = NULL;
-
-  return_string = (char *) calloc(256, sizeof(char));
-  if (! return_string)
-     return NULL;
-
-  //find the maxscore and print out winner
-  for (i=0;i<global->numPlayers;i++)
-    {
-      if (global->players[i]->score == maxscore)
-        {
-          multiwinner=true;
-          if (global->players[i]->team == TEAM_NEUTRAL)
-            neutral_index = i;
-        }
-
-      else if (global->players[i]->score > maxscore)
-        {
-          maxscore = global->players[i]->score;
-          winindex=i;
-          multiwinner=false;
-          if (global->players[i]->team == TEAM_NEUTRAL)
-            neutral_index = i;
-        }
-      if (global->players[i]->team == TEAM_JEDI)
-        jedi_index = i;
-      else if (global->players[i]->team == TEAM_SITH)
-        sith_index = i;
-    }
-
-  //stop mouse during drawing
-  if (! global->os_mouse) show_mouse (NULL);
-
-  //draw background and winner bitmap
-  draw_circlesBG (global, env->db, 0, 0, global->screenWidth, global->screenHeight, false);
-  draw_sprite (env->db, (BITMAP *) global->misc[9], global->halfWidth - 150, global->halfHeight - 150);
-
-  //draw winner names and info about all players
-  int boxtop = global->halfHeight-40;
-  int boxleft = global->halfWidth-200;
-  int boxright = global->halfWidth+280;
-  int boxbottom = boxtop +4+(fonthgt*2)+(fonthgt*global->numPlayers);
-
-  rectfill (env->db, boxleft, boxtop, boxright, boxbottom, BLACK);
-  rect (env->db, boxleft, boxtop, boxright, boxbottom, WHITE);
-  if (multiwinner)
-    {
-      // check for team win
-      if ( global->players[winindex]->team == TEAM_JEDI )
-        {
-          if ( (sith_index >= 0) && ( (global->players[sith_index]->score == global->players[winindex]->score)) )
-            snprintf(return_string, 256, "%s", global->ingame->complete_text[48]);
-          else if ( (neutral_index >= 0) && ( (global->players[neutral_index]->score == global->players[winindex]->score) ) )
-            snprintf(return_string, 256, "%s", global->ingame->complete_text[48]);
-          else
-            snprintf(return_string, 256, "%s", global->ingame->complete_text[45]);
-        }
-      else if ( global->players[winindex]->team == TEAM_SITH )
-        {
-          if ((jedi_index >= 0) && ((global->players[jedi_index]->score == global->players[winindex]->score)))
-            snprintf(return_string, 256, "%s", global->ingame->complete_text[48]);
-          else if ( (neutral_index >= 0) && ( (global->players[neutral_index]->score == global->players[winindex]->score) ) )
-            snprintf(return_string, 256, "%s", global->ingame->complete_text[48]);
-          else
-            snprintf(return_string, 256, "%s", global->ingame->complete_text[46]);
-        }
-      else
-        snprintf(return_string, 256, "%s", global->ingame->complete_text[48]);
-    }
-  else
-     snprintf(return_string, 256, "%s: %s", global->ingame->complete_text[47],
-             global->players[winindex]->getName() );
-
-  textprintf_centre_ex(env->db, font, global->halfWidth, boxtop + 4,
-                       global->players[winindex]->color, -1,
-                       "%s", return_string);
-
-  textout_centre_ex (env->db, font, global->ingame->complete_text[49], global->halfWidth, boxtop+4+fonthgt, WHITE, -1);
-  order = Sort_Scores(global);
-  for (index = 0; index < global->numPlayers; index++)
-    {
-      int i = order[index];
-      int textypos = (index * 10) + boxtop+4+(fonthgt*2);
-      int money;
-
-      textprintf_ex (env->db, font, boxleft+10, textypos , global->players[i]->color, -1, "%s:", global->players[i]->getName ());
-
-      money = 0;
-      for (int weapNum = 0; weapNum < WEAPONS; weapNum++)
-        {
-          int individValue;
-          if (weapon[weapNum].amt)
-            individValue = weapon[weapNum].cost / weapon[weapNum].amt;
-          else
-            individValue = 0;
-          money += (int)(individValue * global->players[i]->nm[weapNum]);
-        }
-      for (int itemNum = 0; itemNum < ITEMS; itemNum++)
-        {
-          int individValue;
-          if (item[itemNum].amt)
-            individValue = item[itemNum].cost / item[itemNum].amt;
-          else
-            individValue = 0;
-          money += (int)(individValue * global->players[i]->ni[itemNum]);
-        }
-      textprintf_ex (env->db, font, boxleft+190, textypos, WHITE, -1, "%3d  $%s   %10d :%2d", global->players[i]->score, Add_Comma(money), global->players[i]->kills, global->players[i]->killed);
-    }
+	bool result = false;
 
-  /*
-  my_quote = global->war_quotes->Get_Random_Line();
-  if (my_quote)
-  {
-     char *little_string;
-     int start_index = 0, to_index = 0;
-     int total_length = strlen(my_quote);
-     int quote_count = 1;
-
-     little_string = (char *) calloc( total_length + 1, sizeof(char));
-     if (little_string)
-     {
-        do
-        {
-           memset(little_string, '\0', total_length + 1);
-           while ((( my_quote[start_index] != ' ' ) || (to_index < 50) ) && (start_index < total_length) )
-           {
-               little_string[to_index] = my_quote[start_index];
-               to_index++;
-               start_index++;
-           }
-          
-           textprintf_ex(env->db,font,boxleft, boxbottom + (10 * quote_count), WHITE, -1,                          "%s", little_string);
-           to_index = 0;
-           quote_count++;
-           while ( (start_index < total_length) && (my_quote[start_index] == ' ') )
-                start_index++;
-        } while (start_index < total_length);
-        free(little_string);
-     }   
-  }
-  //do fade and wait for user keypress
-  change (global, env->db);
-  readkey ();
-  // for (i = 0; i < global->numPlayers; i++)
-  //  global->players[i]->type = global->players[i]->type_saved;
-  */
-  return return_string;
-}
+	if (load_config_file) {
+		snprintf(fullPath, PATH_MAX, "%s/atanks-config.txt", env.configDir);
 
+		FILE* old_config_file = fopen(fullPath, "r");
 
+		if (old_config_file) {
+			env.load_from_file(old_config_file);
 
-void do_quote(GLOBALDATA *global, ENVIRONMENT *env)
-{
-  char *my_quote;
-  int fonthgt = text_height(font)+10;
-  int boxleft = global->halfWidth-200;
-  int boxtop = global->halfHeight-40;
-  int boxbottom = boxtop +4+(fonthgt*2)+(fonthgt*global->numPlayers);
-
-  my_quote = global->war_quotes->Get_Random_Line();
-  if (my_quote)
-  {
-     char *little_string;
-     int start_index = 0, to_index = 0;
-     int total_length = strlen(my_quote);
-     int quote_count = 1;
-
-     little_string = (char *) calloc( total_length + 1, sizeof(char));
-     if (little_string)
-     {
-        do
-        {
-           memset(little_string, '\0', total_length + 1);
-           while ((( my_quote[start_index] != ' ' ) || (to_index < 50) ) && (start_index < total_length) )
-           {
-               little_string[to_index] = my_quote[start_index];
-               to_index++;
-               start_index++;
-           }
-
-           textprintf_ex(env->db,font,boxleft, boxbottom + (10 * quote_count), WHITE, -1,                          "%s", little_string);
-           to_index = 0;
-           quote_count++;
-           while ( (start_index < total_length) && (my_quote[start_index] == ' ') )
-                start_index++;
-        } while (start_index < total_length);
-        free(little_string);
-     }
-  }
-
-  //do fade and wait for user keypress
-  change (global, env->db);
-  readkey ();
-  for (int i = 0; i < global->numPlayers; i++)
-    global->players[i]->type = global->players[i]->type_saved;
+			// over-ride full screen setting with command line
+			if ( (full_screen == FULL_SCREEN_TRUE)
+			  || (full_screen == FULL_SCREEN_FALSE) )
+				env.full_screen = full_screen;
 
-}
+			// Initialize after loading
+			init_game_settings();
 
+			// Load texts first ...
+			env.load_text_files();
 
+			// ...then the players last
+			result = loadPlayers(old_config_file);
 
+			fclose(old_config_file);
+		}
+	} // End of loading old config file
 
-//draws indicaation bar
-void graph_bar (ENVIRONMENT *env, int x, int y, long int col, int actual, int max)
-{
-  rect (env->db, x, y, x + max + 2, y + 8, BLACK);
-  rectfill (env->db, x + 1, y + 1, x + 1 + actual, y + 7, col);
+	return result;
 }
 
 
-//draws indication bar - centred
-void graph_bar_center (ENVIRONMENT *env, int x, int y, long int col, int actual, int max)
+static bool loadPlayers(FILE* file)
 {
-  rect (env->db, x, y, x + max + 2, y + 8, BLACK);
-  rectfill (env->db, x + 1 + max / 2, y + 1, x + 1 + actual + max / 2, y + 7, col);
+	int32_t max_pl = env.numPermanentPlayers;
+
+	if (env.allPlayers) {
+		for (int32_t i = 0; i < env.numPermanentPlayers; ++i) {
+			if (env.allPlayers[i])
+				delete env.allPlayers[i];
+			env.allPlayers[i]  = nullptr;
+		}
+		free(env.allPlayers);
+		env.allPlayers = nullptr;
+	}
+
+	env.allPlayers = (PLAYER **)malloc(sizeof(PLAYER*) * max_pl);
+
+	if (!env.allPlayers) {
+		fprintf(stderr, "%s:%d : Failed to allocate memory for allPlayers\n",
+				__FILE__, __LINE__);
+		return false;
+	}
+
+	for (int32_t i = 0; i < max_pl; ++i)
+		env.allPlayers[i] = nullptr;
+
+	int32_t pl_count = 0;
+	bool    status   = true;
+
+	while (status) {
+		PLAYER* player_new = nullptr;
+		try {
+			player_new = new PLAYER();
+		} catch (std::exception &e) {
+			fprintf(stderr, "%s:%d : Failed to allocate memory for player\n",
+					__FILE__, __LINE__);
+			status = false;
+		}
+
+		if (status)
+			status = player_new->load_from_file(file);
+
+		if (status) {
+			player_new->index = pl_count;
+			env.allPlayers[pl_count++] = player_new;
+			if (pl_count == max_pl) {
+				max_pl += 5;
+				env.allPlayers = (PLAYER**)realloc(env.allPlayers,
+				                                   sizeof(PLAYER *) * max_pl);
+				for (int32_t i = pl_count; i < max_pl; ++i)
+					env.allPlayers[i] = nullptr;
+			}
+		} else if (player_new)
+			delete player_new;
+	} // end of while status
+
+	env.numPermanentPlayers = pl_count;
+
+	return true;
 }
 
 
-//Some global parameters
-int ord;
-void loadshields (ENVIRONMENT *env)
-{
-  TANK *tank;
-  int objCount;
-
-  for (objCount = 0; (tank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && tank; objCount++)
-    tank->reactivate_shield ();
-}
-
-void change_wind_strength (ENVIRONMENT *env)
+static int32_t menu()
 {
-  if (env->windvariation == 0.0 || (int)env->windstrength == 0)
-    {
-      return;
-    }
-  else
-    {
-      env->wind = env->lastwind + (double)(rand () % (int)(env->windvariation * 100)) / 100 - (env->windvariation / 2);
-      if (env->wind > (env->windstrength / 2))
-        {
-          env->wind = env->windstrength / 2;
-        }
-      else if (env->wind < (-env->windstrength / 2))
-        {
-          env->wind = -env->windstrength / 2;
-        }
-
-      env->lastwind = env->wind;
-    }
-
-   // make sure game clients have up to date wind data
-   #ifdef NETWORK
-   char buffer[64];
-   sprintf(buffer, "WIND %f", env->wind);
-   env->_global->Send_To_Clients(buffer);
-   #endif
-}
-
-TANK *nextturn (GLOBALDATA *global, ENVIRONMENT *env, bool skippingComputerPlay)
-{
-  TANK *tank = NULL;
-  int ordCurrently = ord;
-  static int do_wind = 0;
-  static int next_wind = 0;
-  int index = 0, lowest_index = 0, lowest_shots = INT_MAX;
-
-  // check whether there currently *are* active tanks first
-  if (global->numTanks)
-    {
-      // find first tank with lowest number of shots fired
-      while ( index < global->maxNumTanks )
-        {
-          if ( env->order[index] )    // make sure tank exists
-            {
-              if ( env->order[index]->shots_fired < lowest_shots )
-                {
-                  lowest_shots = env->order[index]->shots_fired;
-                  lowest_index = index;
-                }
-            }
-          index++;
-        }     // end of looking for low index
-
-
-      do
-        {
-          ord++;
-          // coming around to the next turn
-          if (ord >= global->maxNumTanks)
-            {
-              ord = 0;
-              doLaunch(global, env);
-              // launch before we change the wind
-              next_wind = 1;
-            }
-
-        }
-      while ((!env->order[ord]) && (ord != ordCurrently));
-      tank = env->order[ord];
-      global->currTank = tank;
-
-      if ( tank->shots_fired > lowest_shots )
-        {
-          tank = env->order[lowest_index];
-          global->currTank = tank;
-        }
-
-      // Wind is blowing :-)
-      // change_wind_strength (env);
-      if ( (global->turntype != TURN_SIMUL) || (do_wind) )
-        {
-          change_wind_strength(env);
-          do_wind = next_wind = 0;
-        }
-      else
-        {
-          do_wind = next_wind;
-          next_wind = 0;
-        }
-    }
-
-  if (tank)
-    {
-      if (!skippingComputerPlay)
-        {
-          env->make_fullUpdate();
-          env->do_updates();
-        }
-      tank->reactivate_shield ();
-      clear_keybuf();
-      if (global->max_fire_time)
-        {
-          tank->player->time_left_to_fire = global->max_fire_time;
-          tank->player->skip_me = false;
-        }
-    }
-
-  return tank;
+	int32_t result     = SIG_OK;
+	int32_t shift_menu = env.halfHeight < 240 ? 240 - env.halfHeight : 0;
+	int32_t move_btn   = env.button[0]->w / 2;
+	int32_t bn         = env.language == EL_RUSSIAN ? MENUBUTTONS * 2 : 0;
+
+	BUTTON but_play(env.halfWidth - move_btn,
+	                env.halfHeight - 235 + shift_menu,
+	                env.button[bn], env.button[bn], env.button[bn + 1]);
+	bn += 2;
+	BUTTON but_help(env.halfWidth - move_btn,
+	                env.halfHeight - 185 + shift_menu,
+	                env.button[bn], env.button[bn], env.button[bn + 1]);
+	bn += 2;
+	BUTTON but_options(env.halfWidth - move_btn,
+	                   env.halfHeight - 135 + shift_menu,
+	                   env.button[bn], env.button[bn], env.button[bn + 1]);
+	bn += 2;
+	BUTTON but_players(env.halfWidth - move_btn,
+	                   env.halfHeight - 85 + shift_menu,
+	                   env.button[bn], env.button[bn], env.button[bn + 1]);
+	bn += 2;
+	BUTTON but_credits(env.halfWidth - move_btn,
+	                   env.halfHeight - 35 + shift_menu,
+	                   env.button[bn], env.button[bn], env.button[bn + 1]);
+	bn += 2;
+	BUTTON but_quit(env.halfWidth - move_btn,
+	                env.halfHeight + 65 + shift_menu,
+	                env.button[bn], env.button[bn], env.button[bn + 1]);
+	bn += 2;
+	BUTTON but_network(env.halfWidth - move_btn,
+	                   env.halfHeight + 15 + shift_menu,
+	                   env.button[bn], env.button[bn], env.button[bn + 1]);
+
+	BUTTON *button[MENUBUTTONS] = { &but_play,    &but_help,    &but_options,
+	                                &but_players, &but_credits, &but_network,
+	                                &but_quit };
+
+	// Initialization of the menu
+	global.stopwindow = true;
+	fi = 1;
+	lx = 0;
+	ly = 0;
+	k  = 0;
+	K  = 0;
+
+	bool    done         = false;
+	int32_t seconds_idle = 0;
+	int32_t btn_over     = -1;
+	int32_t currentindex = 0;
+	int32_t oldindex     = 0;
+	int32_t maxindex     = MENUBUTTONS;
+	int32_t lastmouse_x  = 0;
+	int32_t lastmouse_y  = 0;
+
+	// Clear key buffer and erase mouse button presses
+	while ( keypressed() )
+		readkey();
+	mouse_b = 0;
+
+	// Enable first background drawing:
+	bool need_draw = true;
+	draw_simple_bg(true);
+	global.make_fullUpdate();
+
+	while ( !done && (SIG_OK == result) ) {
+
+		// Extra loop to divide the handling and the drawing
+		while (!done && !need_draw) {
+			// Count seconds for demo mode to start after its wait time
+			if ( global.check_time_changed() ) {
+				if (++seconds_idle > DEMO_WAIT_TIME) {
+					done = true;
+					global.set_command(GLOBAL_COMMAND_DEMO);
+				}
+			}
+
+			// Detect mouse movement for custom cursors
+			if (!env.osMouse
+			  && ( (lastmouse_x != mouse_x)
+			    || (lastmouse_y != mouse_y) ) ) {
+				lastmouse_x = mouse_x;
+				lastmouse_y = mouse_y;
+				need_draw   = true;
+			}
+
+			// See where the mouse is
+			for (int32_t z = 0; z < MENUBUTTONS; z++) {
+				if (button[z]->isMouseOver()) {
+					if ( (btn_over > -1) && (btn_over != z) ) {
+						button[z]->draw();
+						need_draw = true;
+					}
+
+					btn_over = z;
+					break;
+				}
+			}
+
+			// Handle mouse click
+			if (mouse_b & 1) {
+				for (int32_t z = 0; z < MENUBUTTONS; z++) {
+					if (button[z]->isPressed ()) {
+						need_draw = true;
+						done      = true;
+						if (z == 0)
+							global.set_command(GLOBAL_COMMAND_PLAY);
+						else if (z == 1)
+							global.set_command(GLOBAL_COMMAND_HELP);
+						else if (z == 2)
+							global.set_command(GLOBAL_COMMAND_OPTIONS);
+						else if (z == 3)
+							global.set_command(GLOBAL_COMMAND_PLAYERS);
+						else if (z == 4)
+							global.set_command(GLOBAL_COMMAND_CREDITS);
+						else if (z == 5)
+							global.set_command(GLOBAL_COMMAND_NETWORK);
+						else if (z == 6) {
+							global.set_command(GLOBAL_COMMAND_QUIT);
+							result = SIG_QUIT_GAME;
+						}
+					}
+				}
+			} // End of mouse button pressed
+
+			// check for key press
+			if ( keypressed() ) {
+				k = readkey();
+				K = k >> 8;
+				fi = 2;
+			}
+
+			// Move selection down
+			if ( ( K == KEY_DOWN ) || (K == KEY_S) ) {
+				if (++currentindex >= maxindex)
+					currentindex = 0;
+				need_draw = true;
+			}
+
+			// Move selection up
+			else if ( (K == KEY_UP) || (K == KEY_W) ) {
+				if (--currentindex < 0)
+					currentindex = maxindex - 1;
+				need_draw = true;
+			}
+
+			// Activate selection
+			else if ( (KEY_ENTER     == K)
+				   || (KEY_ENTER_PAD == K)
+				   || (KEY_SPACE     == K) ) {
+				need_draw = true;
+				done      = true;
+				if (currentindex == 0)
+					global.set_command(GLOBAL_COMMAND_PLAY);
+				else if (currentindex == 1)
+					global.set_command(GLOBAL_COMMAND_HELP);
+				else if (currentindex == 2)
+					global.set_command(GLOBAL_COMMAND_OPTIONS);
+				else if (currentindex == 3)
+					global.set_command(GLOBAL_COMMAND_PLAYERS);
+				else if (currentindex == 4)
+					global.set_command(GLOBAL_COMMAND_CREDITS);
+				else if (currentindex == 5)
+					global.set_command(GLOBAL_COMMAND_NETWORK);
+				else if (currentindex == 6)
+					global.set_command(GLOBAL_COMMAND_QUIT);
+			}
+
+			// Quick keys to exit and handle close button of the window
+			else if ( (KEY_Q      == K)
+				   || (KEY_ESC == K)) {
+				done   = true;
+				result = SIG_QUIT_GAME;
+			}
+
+			// Erase key presses
+			K = 0;
+
+			// Print out update info if any
+			if ( (global.update_string) && (global.update_string[0]) ) {
+				textout_centre_ex (global.canvas, font, global.update_string,
+								   env.halfWidth    - 20, env.screenHeight - 50,
+								   WHITE, -1);
+				global.make_update(50, 450, 300, 50);
+				need_draw = true;
+			}
+
+			// Print out client messages
+			if (global.client_message) {
+				textout_centre_ex(global.canvas, font, global.client_message,
+								  env.halfWidth    - 20, env.screenHeight - 25,
+								  WHITE, -1);
+				global.make_update(50, 450, 300, 100);
+				need_draw = true;
+			}
+
+			// Sleep a bit if nothing happened
+			if (!done && !need_draw)
+				LINUX_SLEEP
+		} // End of while not needing to draw
+
+
+		// flip to front if needed
+		if (need_draw) {
+
+			// Draw the buttons
+			SHOW_MOUSE(nullptr)
+			draw_simple_bg(true);
+
+			for (int32_t z = 0; z < MENUBUTTONS; z++) {
+				button[z]->draw();
+
+				// draw a rectangle around the selected button
+				if ( (z == currentindex) || (z == oldindex) ) {
+					int32_t left   = env.halfWidth - move_btn - 6;
+					int32_t top    = env.halfHeight - 241 + (50 * currentindex)
+					               + shift_menu;
+					int32_t right  = env.halfWidth + move_btn + 5;
+					int32_t bottom = env.halfHeight - 192 + (50 * currentindex)
+					               + shift_menu;
+					global.make_update(left, top, right - left, bottom - top);
+					if (z == currentindex)
+						rect(global.canvas, left, top, right, bottom, YELLOW);
+				}
+			} // end of looping buttons
+
+			// Show non-OS mouse
+			SHOW_MOUSE(global.canvas)
+
+			global.do_updates();
+			need_draw = false;
+			oldindex  = currentindex;
+		} // End of if need_draw
+	} // End of menu loop
+
+	clear_keybuf ();
+
+	return result;
 }
 
-void showRoundEndScoresAt (GLOBALDATA *global, ENVIRONMENT *env, BITMAP *bitmap, int x, int y, int winner)
-{
-  int z;
-
-  env->make_update (x - 150, y - 100, 301, 301);
-  rectfill (bitmap, x - 150, y - 100, x + 100, y + 100, BLACK);
-  rect (bitmap, x - 150, y - 100, x + 100, y + 100, WHITE);
-  if (winner == JEDI_WIN)
-    textout_centre_ex (bitmap, font, "Jedi", x - 20, y - 90, WHITE, -1);
-  else if (winner == SITH_WIN)
-    textout_centre_ex (bitmap, font, "Sith", x - 20, y - 90, WHITE, -1);
-  else if (winner == -2)
-    textout_centre_ex (bitmap, font, "Draw", x - 20, y - 90, WHITE, -1);
-  else
-    textprintf_centre_ex (bitmap, font, x - 30, y - 90, global->players[winner]->color, -1, "%s: %s", global->ingame->complete_text[47], global->players[winner]->getName ());
-
-  textout_centre_ex (bitmap, font, global->ingame->complete_text[50], x - 30, y - 70, WHITE, -1);
-  for (z = 0; z < global->numPlayers; z++)
-    {
-      textprintf_ex (bitmap, font, x - 140, (z * 10) + y - 50, global->players[z]->color, -1, "%s:", global->players[z]->getName ());
-      textprintf_ex (bitmap, font, x + 60, (z * 10) + y - 50, WHITE, -1, "%d", global->players[z]->score);
-    }
-}
 
-int setSlideColumnDimensions (GLOBALDATA *global, ENVIRONMENT *env, int x, bool reset)
+static void newgame()
 {
-  int pixelHeight;
-  char	*done	= env->done;
-  int	*dropTo = env->dropTo;
-  int	*h	= env->h;
-  int	*fp	= env->fp;
-  double	*velocity = env->velocity;
-  double	*dropIncr = env->dropIncr;
-
-  if (x < 0 || x > (global->screenWidth-1))
-    {
-      return (0);
-    }
-
-  if (reset)
-    {
-      h[x] = 0;
-      fp[x] = 0;
-      dropTo[x] = global->screenHeight - 1;
-    }
-  done[x] = 0;
-
-  // Calc the top and bottom of the column to slide
-
-  // Find top-most non-PINK pixel
-  for (pixelHeight = h[x]; pixelHeight < dropTo[x]; pixelHeight++)
-    if (getpixel (env->terrain, x, pixelHeight) != PINK)
-      break;
-  h[x] = pixelHeight;
-  env->surface[x] = pixelHeight;
-
-  // Find bottom-most PINK pixel
-  for (pixelHeight = dropTo[x]; pixelHeight > h[x]; pixelHeight--)
-    if (getpixel (env->terrain, x, pixelHeight) == PINK)
-      break;
-  dropTo[x] = pixelHeight;
-
-  // Find bottom-most unsupported pixel
-  for (; pixelHeight >= h[x]; pixelHeight--)
-    if (getpixel (env->terrain, x, pixelHeight) != PINK)
-      break;
-
-  // If there's some processing to do
-  if ((pixelHeight >= h[x]) && (h[x] < dropTo[x]))
-    {
-      fp[x] = pixelHeight - (int)h[x] + 1;
-      return (0);
-    }
-  else
-    {
-      if (velocity[x])
-        play_sample ((SAMPLE *) global->sounds[10], env->scaleVolume((velocity[x] / 10) * 255), (int)((double)(x - global->halfWidth) / global->halfWidth * 128 + 128), 1000 - (int)((double)fp[x] / global->screenHeight) * 1000, 0);
-      h[x] = 0;
-      fp[x] = 0;
-      done[x] = 1;
-      velocity[x] = 0;
-      dropIncr[x] = 0;
-      dropTo[x] = global->screenHeight - 1;
-      return (1);
-    }
-  return (0);
+	env.initialise ();
+	global.initialise ();
+
+	// if a game should be loaded, try it or deny loading of the game
+	if ( (env.loadGame) && (!Load_Game()) )
+		env.loadGame = false;
+
+	// Now check back whether to load a game
+	if (!env.loadGame)
+		initialisePlayers ();
+
+	// There must not be any tanks!
+	TANK* tank      = nullptr;
+	TANK* next_tank = nullptr;
+	global.getHeadOfClass(CLASS_TANK, &tank);
+	while (tank) {
+		tank->getNext(&next_tank);
+		tank->player = nullptr;
+		delete tank;
+		tank         = next_tank;
+	}
+
+	// This is always true here, as a newly started game is handled like a loaded one:
+	env.isGameLoaded = true;
 }
 
 
-int drawFracture (GLOBALDATA *global, ENVIRONMENT *env, BITMAP *dest, BOX *updateArea, int x, int y, int angle, int width, int segmentLength, int maxRecurse, int recurseDepth)
+/// @brief parse arguments and return EXIT_SUCCESS on success or EXIT_FAILURE
+/// if anything went wrong. If all is well but help was requested, return
+/// EXIT_HELP_SHOWN
+static int32_t parse_args(int32_t argc, char** argv)
 {
-  int branchCount;
-  int x1, x2, x3;
-  int y1, y2, y3;
-
-  x1 = (int)(x + global->slope[angle][0] * width);
-  y1 = (int)(y + global->slope[angle][1] * width);
-  x2 = (int)(x - global->slope[angle][0] * width);
-  y2 = (int)(y - global->slope[angle][1] * width);
-  x3 = (int)(x + global->slope[angle][1] * segmentLength);
-  y3 = (int)(y + global->slope[angle][0] * segmentLength);
-  triangle (dest, x1, y1, x2, y2, x3, y3, PINK);
-
-  if (recurseDepth == 0)
-    {
-      updateArea->x = x1;
-      updateArea->y = y1;
-      updateArea->w = x1;
-      updateArea->h = y1;
-    }
-  updateArea->x = MIN (MIN (MIN (x1, x2), x3), updateArea->x);
-  updateArea->y = MIN (MIN (MIN (y1, y2), y3), updateArea->y);
-  updateArea->w = MAX (MAX (MAX (x1, x2), x3), updateArea->w);
-  updateArea->h = MAX (MAX (MAX (y1, y2), y3), updateArea->h);
-
-  if (recurseDepth < maxRecurse)
-    {
-      for (branchCount = 0; branchCount < 3; branchCount++)
-        {
-          if ((branchCount == 0) || (Noise (x + y + branchCount) < 0))
-            {
-              int newAngle, reduction;
-              newAngle = (angle + (int)(Noise (x + y + 4) * 30));
-              while (newAngle < 0)
-                newAngle += 360;
-              newAngle %= 360;
-
-              reduction = 2;
-              if (branchCount == 1)
-                {
-                  newAngle = (int)(angle + 90 +
-                                   (Noise (x + y + 25 + branchCount) * 22.5)) % 360;
-                  reduction = abs ((int)Noise (x + y + 1 + branchCount) * 4 + 3);
-                }
-              else if (branchCount == 2)
-                {
-                  newAngle = (int)(angle + 270 +
-                                   (Noise (x + y + 32 + branchCount) * 22.5)) % 360;
-                  reduction = abs ((int)Noise (x + y + 2 + branchCount) * 4 + 3);
-                }
-              drawFracture (global, env, dest, updateArea, x3, y3, newAngle, width / reduction, segmentLength / reduction, maxRecurse, recurseDepth + 1);
-            }
-        }
-    }
-
-  // Calculate width and height, previously right and bottom
-  if (recurseDepth == 0)
-    {
-      updateArea->w -= updateArea->x;
-      updateArea->h -= updateArea->y;
-    }
-
-  return (0);
-}
-
-/*
-void initSurface (GLOBALDATA *global, ENVIRONMENT *env)
-{
-  int pixelHeight;
-  for (int x = 0; x < global->screenWidth; x++)
-    {
-      for (pixelHeight = 0; pixelHeight < global->screenHeight; pixelHeight++)
-        if (getpixel (env->terrain, x, pixelHeight) != PINK)
-          break;
-      env->surface[x] = pixelHeight;
-    }
+	for (int32_t c = 1; c < argc; ++c)  {
+		bool        has_value = true;
+		std::string arg(argv[c]);
+
+		if ( (arg == SWITCH_HELP) || (arg == "--help") ) {
+			print_text_help();
+			return HELP_REQUESTED;
+		} else if (arg == SWITCH_FULL_SCREEN) {
+			screen_mode = GFX_AUTODETECT_FULLSCREEN;
+			full_screen = FULL_SCREEN_TRUE;
+		} else if (arg == SWITCH_WINDOWED) {
+			screen_mode = GFX_AUTODETECT_WINDOWED;
+			full_screen = FULL_SCREEN_FALSE;
+		} else if ( (arg == "-d") || (arg == "--depth") ) {
+			if ( (c < (argc - 1)) && (argv[c + 1][0] != '-') ) {
+				std::string next_arg(argv[++c]);
+				int32_t     val = strtol (next_arg.c_str(), nullptr, 10);
+
+				if ( (16 == val) || (32 == val) )
+					env.colourDepth = val;
+				else {
+					cerr << "ERROR: Invalid graphics depth!\n"
+					     << "       Only 16 or 32 bit are supported!" << endl;
+					return EXIT_FAILURE;
+				}
+			} else
+				has_value = false;
+		} else if ( (arg == "-w") || (arg == "--width") ) {
+			if ( (c < (argc - 1)) && (argv[c + 1][0] != '-') ) {
+				std::string next_arg(argv[++c]);
+				int32_t     val = strtol (next_arg.c_str(), nullptr, 10);
+
+				if ( 512 <= val ) {
+					env.screenWidth      = val;
+					env.halfWidth        = env.screenWidth / 2;
+					env.temp_screenWidth = env.screenWidth;
+				} else {
+					cerr << "ERROR: Width too small (minimum 512)\n" << endl;
+					return EXIT_FAILURE;
+				}
+			} else
+				has_value = false;
+		} else if ((arg == "-t") || (arg == "--tall") || (arg == "--height")) {
+			if ( (c < (argc - 1)) && (argv[c + 1][0] != '-') ) {
+				std::string next_arg(argv[++c]);
+				int32_t     val = strtol (next_arg.c_str(), nullptr, 10);
+
+				if ( 320 <= val ) {
+					env.screenHeight      = val;
+					env.halfHeight        = env.screenHeight / 2;
+					env.temp_screenHeight = env.screenHeight;
+				} else {
+					cerr << "ERROR: Height too small (minimum 320)\n" << endl;
+					return EXIT_FAILURE;
+				}
+			} else
+				has_value = false;
+		} else if (arg == "--datadir") {
+			if ( (c < (argc - 1)) && (argv[c + 1][0] != '-') ) {
+				std::string next_arg(argv[++c]);
+
+				if ( next_arg.length() <= PATH_MAX )
+					strncpy(env.dataDir, next_arg.c_str(), PATH_MAX);
+				else {
+					cerr << "ERROR: Datadir path too long:\n"
+					     << "\"" << next_arg << "\"\n\n"
+					     << "Maximum length:" << PATH_MAX << " characters"
+					     << endl;
+					return EXIT_FAILURE;
+				}
+			} else
+				has_value = false;
+		} else if (arg == "-c") {
+			if ( (c < (argc - 1)) && (argv[c + 1][0] != '-') ) {
+				std::string next_arg(argv[++c]);
+
+				if ( next_arg.length() <= PATH_MAX )
+					strncpy(env.configDir, next_arg.c_str(), PATH_MAX);
+				else {
+					cerr << "ERROR: Configuration path too long:\n"
+					     << "\"" << next_arg << "\"\n\n"
+					     << "Maximum length:" << PATH_MAX << " characters"
+					     << endl;
+					return EXIT_FAILURE;
+				}
+			} else
+				has_value = false;
+		} else if (arg == "--noconfig")
+			load_config_file = false;
+		else if (arg == "--nosound")
+			env.sound_enabled = false;
+		else if (arg == "--noname")
+			env.nameAboveTank = false;
+		else if (arg == "--nonetwork")
+			allow_network = false;
+		else if (arg == "--nobackground")
+			env.drawBackground = false;
+		else if (arg == "--nothread")
+			cout << "--nothread is deprecated and will be ignored." << endl;
+		else if (arg == "--thread")
+			cout << "--thread is deprecated and will be ignored." << endl;
+
+		// If a required argument is missing, print out a message
+		if (!has_value) {
+			cerr << "ERROR: Missing argument for " << arg << endl;
+			return EXIT_FAILURE;
+		}
+	}
+
+	return EXIT_SUCCESS;
 }
-*/
-
-
-int slideLand (GLOBALDATA *global, ENVIRONMENT *env)
-{
-  char	*done	= env->done;
-  int	*dropTo = env->dropTo;
-  int	*h	= env->h;
-  int	*fp	= env->fp;
-  double	*velocity = env->velocity;
-  double	*dropIncr = env->dropIncr;
-  int zz;
-  double land_slide_type = LANDSLIDE_INSTANT;
-
-  // land-slide, make it fall etc.
-  int allDone = 1;
-  if ( (env->landSlideType == LANDSLIDE_NONE) ||
-       (env->landSlideType == LANDSLIDE_TANK_ONLY) )
-    return (allDone);
-
-  else if (env->landSlideType == LANDSLIDE_CARTOON)
-    {
-      if (env->time_to_fall > 0)
-        land_slide_type = LANDSLIDE_CARTOON;
-      else
-        land_slide_type = LANDSLIDE_GRAVITY;
-    }
-
-  else if (env->landSlideType == LANDSLIDE_GRAVITY)
-    land_slide_type = LANDSLIDE_GRAVITY;
-
-  if (land_slide_type == LANDSLIDE_CARTOON)
-    return (allDone);
-
-  for (zz = 0; zz < global->screenWidth; zz++)
-    {
-      if (!done[zz])
-        {
-          allDone = 0;
-          if (land_slide_type == LANDSLIDE_GRAVITY)
-            {
-              if (fp[zz] > 0)
-                {
-                  velocity[zz] += env->gravity;
-                  dropIncr[zz] += velocity[zz];
-                  if (dropIncr[zz] >= 1)
-                    {
-                      if (dropIncr[zz] > dropTo[zz] - (h[zz] + fp[zz]))
-                        {
-                          dropIncr[zz] = dropTo[zz] - (h[zz] + fp[zz]) + 1;
-                        }
-                      blit (env->terrain, env->terrain, zz, h[zz] - (int)dropIncr[zz], zz, h[zz], 1, fp[zz] + (int)dropIncr[zz]);
-                      env->make_bgupdate (zz, h[zz] - (int)dropIncr[zz], 1, fp[zz] + ((int)dropIncr[zz] * 2) + 1);
-                      env->make_update (zz, h[zz] - (int)dropIncr[zz], 1, fp[zz] + ((int)dropIncr[zz] * 2) + 1);
-                      h[zz] += (int)dropIncr[zz];
-                      dropIncr[zz] -= (int)dropIncr[zz];
-                    }
-                  setSlideColumnDimensions (global, env, zz, FALSE);
-                }
-              else
-                {
-                  setSlideColumnDimensions (global, env, zz, FALSE);
-                }
-            }
-          else if (land_slide_type == LANDSLIDE_INSTANT)
-            {
-              if (fp[zz] > 0)
-                {
-                  env->make_bgupdate (zz, h[zz], 1, dropTo[zz] - h[zz] + 1);
-                  env->make_update (zz, h[zz], 1, dropTo[zz] - h[zz] + 1);
-                  done[zz] = 1;
-                  blit (env->terrain, env->terrain, zz, h[zz], zz, dropTo[zz] - fp[zz] + 1, 1, fp[zz]);
-                  vline (env->terrain, zz, h[zz], dropTo[zz] - fp[zz], PINK);
-                }
-              setSlideColumnDimensions (global, env, zz, FALSE);
-            }
-        }
-    }
 
-  return (allDone);
-}
 
-void drawTopBar (GLOBALDATA *global, ENVIRONMENT *env, BITMAP *dest)
+static void play_demo()
 {
-  TANK *tank = global->currTank;
-  char *name = "";
-  int color = 0;
-  int wind_col1 = 0, wind_col2 = 0;
-  static int change_weapon_colour = RED;
-  char *team_name = "";
-  int time_to_fire = 0;
-  int minus_font = (font == global->unicode) ? 9 : 0;
-
-  if (tank)
-    {
-      name = global->currTank->player->getName ();
-      color = global->currTank->player->color;
-      team_name = global->currTank->player->Get_Team_Name();
-      time_to_fire = tank->player->time_left_to_fire;
-    }
-
-  global->updateMenu = 0;
-  blit (global->gfxData.topbar, dest, 0, 0, 0, 0, global->screenWidth, MENUHEIGHT);
-
-  if (tank)
-    {
-      textout_ex (dest, font, name, 2, 2 - minus_font, BLACK, -1);
-      textout_ex (dest, font, name, 1, 1 - minus_font, color, -1);
-      textprintf_ex (dest, font, 1, 11 - minus_font, BLACK, -1, "%s", global->ingame->complete_text[18]);
-      graph_bar_center (env, 50, 11, color, -(tank->a - 180) / 2, 180 / 2);
-      // 0 is directly left, 180 points directly right
-      textprintf_ex (dest, font, 150, 11 - minus_font, BLACK, -1, "%d", 180 - (tank->a - 90));
-
-      textprintf_ex (dest, font, 1, 21 - minus_font, BLACK, -1, "%s", global->ingame->complete_text[19]);
-      graph_bar (env, 50, 20, color, (tank->p) / (MAX_POWER/90), 90);
-      textprintf_ex (dest, font, 150, 21 - minus_font, BLACK, -1, "%d", tank->p);
-      textprintf_ex (dest, font, 200, 21 - minus_font, BLACK, -1, "%s: %s", global->ingame->complete_text[20], team_name);
-      if (tank->cw < WEAPONS)
-      {
-          int weapon_amount;
-
-          if (! weapon[tank->cw].delay )
-              weapon_amount = tank->player->nm[tank->cw];
-          else
-              weapon_amount = tank->player->nm[tank->cw] / weapon[tank->cw].delay;
-
-          if (tank->player->changed_weapon)
-            {
-              textprintf_ex (dest, font, 180, 1, change_weapon_colour, -1, "%s: %d",
-                             weapon[tank->cw].name, weapon_amount);
-              // tank->player->nm[tank->cw]);
-              if (change_weapon_colour == RED)
-                change_weapon_colour = WHITE;
-              else
-                change_weapon_colour = RED;
-
-
-            }
-
-          else
-            textprintf_ex (dest, font, 180, 1, BLACK, -1,"%s: %d",
-                           weapon[tank->cw].name, weapon_amount);
-
-        }      // end of less than WEAPONS
-      else
-        {
-          textprintf_ex (dest, font, 180, 1, BLACK, -1, "%s: %d",
-                         item[tank->cw - WEAPONS].name, tank->player->ni[tank->cw - WEAPONS]);
-        }
-      draw_sprite (env->db, (BITMAP *) global->stock[ (tank->cw) ? tank->cw : 1], 700, 1);
-      textprintf_ex (dest, font, 350, 1, BLACK, -1, "$%s", Add_Comma(tank->player->money));
-      textprintf_ex (dest, font, 350, 12, BLACK, -1, "%s: %d", global->ingame->complete_text[21], tank->player->ni[ITEM_FUEL]);
-    }
-
-  textprintf_ex ( dest, font, 500,  1 - minus_font, BLACK, -1, "%s %d/%d", 
-                  global->ingame->complete_text[12],
-                  (int)(global->rounds - global->currentround) + 1, (int)global->rounds);
-
-  if (global->tank_status[0])
-    textprintf_ex(dest, font, 350, 21, global->tank_status_colour, -1, "%s",
-                  global->tank_status);
-
-  if (env->windstrength > 0)
-    {
-      textprintf_ex (dest, font, 500, 11 - minus_font, BLACK, -1, "%s", global->ingame->complete_text[22]);
-      if (env->wind > 0)
-        {
-          wind_col1 = 1;
-          wind_col2 = 0;
-        }
-      if (env->wind < 0)
-        {
-          wind_col1 = 0;
-          wind_col2 = 1;
-        }
-      rect (dest, 540, 12, (int)(540 + env->windstrength * 4 + 2), 18, BLACK);
-      rectfill (dest, (int)(541 + env->windstrength * 2), 13,
-                (int) (541 + env->wind * 4 + env->windstrength * 2), 17,
-                makecol (200 * wind_col1, 200 * wind_col2, 0));
-    }
-
-  if (global->max_fire_time)
-    textprintf_ex( dest, font, 500, 20, BLACK, -1, "Time: %d", time_to_fire);
-
-  global->stopwindow = 1;
-  env->make_update (0, 0, global->screenWidth, MENUHEIGHT);
-  global->stopwindow = 0;
+	int32_t old_skip   = env.skipComputerPlay;
+	int32_t old_rounds = env.rounds;
+	bool    old_music  = env.play_music;
+
+	global.demo_mode = true;
+	env.loadGame     = false;
+	env.play_music   = false;
+
+	env.rounds          = (rand() % 101) + (rand() % 101) + 50;
+	global.currentround = env.rounds - (rand() % env.rounds);
+
+	// Be sure to have at least 10 rounds left
+	if (global.currentround < 10)
+		global.currentround = 10;
+
+	// And at least 10 rounds must have been played, or it'll be a bit boring
+	if (global.currentround > (env.rounds - 10))
+		global.currentround =  env.rounds - 10;
+
+	env.skipComputerPlay = SKIP_NONE;
+
+	// set up a bunch of players (non-human, less than 10)
+	int32_t playerCount = 0;
+	env.numGamePlayers  = 0;
+	for (int32_t i = 0; i < env.numPermanentPlayers; ++i) {
+		if ( (env.allPlayers[i]->type > HUMAN_PLAYER)
+		  && (i < MAXPLAYERS) ) {
+			env.addGamePlayer(env.allPlayers[i]);
+			playerCount++;
+		}
+	}
+
+	newgame();
+
+	for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+		env.players[i]->newGame();
+
+		// give them money to spend:
+		env.players[i]->money += static_cast<int32_t>(env.players[i]->type)
+							   * 25000 * (env.rounds - global.currentround);
+	}
+
+	while ( (global.currentround > 0) && (!global.isCloseBtnPressed()) ) {
+		game();
+		if ( (global.get_command() == GLOBAL_COMMAND_QUIT)
+		  || (global.get_command() == GLOBAL_COMMAND_MENU) )
+			break;
+	}
+
+	endgame_cleanup();
+	global.demo_mode     = false;
+	env.skipComputerPlay = old_skip;
+	env.play_music       = old_music;
+	env.rounds           = old_rounds;
 }
 
-/*
- *  Calculate the effective damage for a given weapon.
- *  Recursively add the damage of sub-munitions, factor in chaos
- *    and munition density.
- */
-long int calcTotalEffectiveDamage (int weapNum)
-{
-  WEAPON *weap = &weapon[weapNum];
-  long int total = 0;
-
-  if (weap->submunition >= 0)
-    {
-      WEAPON *subm = &weapon[weap->submunition];
-
-      // How chaotic is this weapon?
-      double chaosVal=(weap->spreadVariation +
-                       weap->speedVariation +
-                       subm->countVariation) / 3;
-      double coverage = (weap->numSubmunitions *
-                         subm->radius) /
-                        (double)weap->radius;
-
-      total += calcTotalEffectiveDamage (weap->submunition) *
-               weap->numSubmunitions;
-      total = (long int)(total * coverage / weap->numSubmunitions);
-      total -= (long int)((total / 2) * (1.0 - chaosVal));
-    }
-  else
-    {
-      total += weap->damage;
-    }
-
-  return (total);
-}
 
-/*
- *  Calculate the potential damage for a given weapon.
- *  Recursively add the damage of sub-munitions.
- */
-long int calcTotalPotentialDamage (int weapNum)
+static void play_local()
 {
-  WEAPON *weap = &weapon[weapNum];
-  long int total = 0;
-
-  if ( (weap->submunition >= 0) && (weap->numSubmunitions > 0) )
-    total += calcTotalPotentialDamage (weap->submunition) *
-             weap->numSubmunitions;
-  else
-    total += weap->damage;
-
-  return (total);
+	if (selectPlayers() != MRC_Esc_Menu) {
+
+		// make sure the game has a name
+		if (!env.game_name[0])
+			strncpy(env.game_name, env.ingame->Get_Line(53), GAMENAMELEN);
+
+		newgame ();
+
+		if (!env.loadGame) {
+			global.currentround = env.rounds;
+			for (int32_t i = 0; i < env.numGamePlayers; ++i)
+				env.players[i]->newGame();
+		}
+
+		// play the game for the selected number of rounds
+		while ( (global.currentround > 0) && (!global.isCloseBtnPressed()) ) {
+			game (); // play a round
+
+			if (env.background_music) {
+				stop_sample(env.background_music);
+			}
+
+			if (global.isCloseBtnPressed())
+				global.set_command(GLOBAL_COMMAND_QUIT);
+
+			// if user selected to quit or return to main menu during game play
+			if ( (global.get_command() == GLOBAL_COMMAND_QUIT)
+			  || (global.get_command() == GLOBAL_COMMAND_MENU) ) {
+				env.sendToClients("CLOSE");
+				break;
+			}
+
+			if (global.currentround != 0)
+				// end of the round
+				env.sendToClients("ROUNDEND");
+		}
+
+		// only show winner if finished all rounds and not broken off the last
+		// round by exiting or quitting
+		if ( (global.currentround == 0)
+		  && (global.get_command() == GLOBAL_COMMAND_PLAY) ) {
+			char        buffer[256] = { 0 };
+			const char* winner      = do_winner();
+
+			if (winner)
+				snprintf(buffer, 255, "GAMEEND The game went to %s.", winner);
+			else
+				strncpy(buffer, "GAMEEND", 255);
+
+			env.sendToClients(buffer);
+
+			// Do fade and wait for user keypress
+			quickChange(true);
+			readkey ();
+
+			for (int i = 0; i < env.numGamePlayers; i++)
+				env.players[i]->type = env.players[i]->type_saved;
+		}
+		endgame_cleanup ();
+	} // end of start new game
 }
 
-void doNaturals (GLOBALDATA *global, ENVIRONMENT *env)
-{
-  int chance;
-
-  if (env->naturals_since_last_shot >= 5)
-    return;
-
-  if (env->lightning)
-    {
-      chance = (int)(600 / env->lightning) + 100;
-      if (!(rand () % chance))
-        {
-          BEAM *newbeam;
-          int ca = ((rand () % 160) + (360 - 80)) % 360;
-
-          newbeam = new BEAM (global, env,
-                              rand () % global->screenWidth, 0,
-                              ca, SML_LIGHTNING + (rand () % (int)env->lightning));
-          if (newbeam)
-          {
-            newbeam->player = NULL;
-            env->naturals_since_last_shot++;
-          }
-          else
-              perror ( "atanks.cc: Failed allocating memory for newbeam in doNaturals");
-        }
-    }      // end of lightning
-
-  // only create meteors  if we are not in aim mode on simul turn type
-  if ( (global->turntype == TURN_SIMUL) && (env->stage == STAGE_AIM) )
-    return;
-
-  if (env->meteors)
-    {
-      chance = (int)(600 / env->meteors) + 100;
-      if (!(rand () % chance))
-        {
-          MISSILE *newmis;
-          int ca = ((rand () % 160) + (360 - 80)) % 360;
-          double mxv = global->slope[ca][0] * 5;
-          double myv = global->slope[ca][1] * 5;
-
-          newmis = new MISSILE(global, env,
-                               rand () % global->screenWidth, 0,
-                               mxv, myv, SML_METEOR + (rand () % (int)env->meteors));
-          if (newmis)
-          {
-            newmis->player = NULL;
-            env->naturals_since_last_shot++;
-          }
-          else
-              perror ( "atanks.cc: Failed allocating memory for newmis in doNaturals");
-        }
-    }
-
-  if (env->falling_dirt_balls)
-    {
-      chance = (int) (600 / env->falling_dirt_balls) + 100;
-      if (! (rand() % chance) )
-        {
-          MISSILE *newmis;
-          int ca = ((rand() % 100) + (360 - 80) ) % 360;
-          double mxv = global->slope[ca][0] * 5;
-          double myv = global->slope[ca][1] * 5;
-
-          newmis = new MISSILE(global, env,
-                               rand() % global->screenWidth, 0,
-                               mxv, myv, DIRT_BALL + ( rand() % (int) env->falling_dirt_balls) );
-          if (newmis)
-          {
-            newmis->player = NULL;
-            env->naturals_since_last_shot++;
-          }
-          else
-              perror( "atanks.cc: Failed to allocate memory for falling dirt ball in doNaturals");
-        }
-    }
-}
 
-#ifdef OLD_GAMELOOP
-void game (GLOBALDATA *global, ENVIRONMENT *env)
+static void play_networked()
 {
-  int tanksfall;
-  int tanklife, tlt, dclock;
-  int lb, ca;
-  int allDone, anyExploding, anyTeleporting, anyLaserFiring;
-  int roundEndCount = 0;
-  bool nextTankSelected = false;
-  int humanPlayers = 0;
-  int skippingComputerPlay = FALSE;
-  int team_won = NO_WIN;
-  bool bWinnerIsCredited = false;
-  int my_class;
-  VIRTUAL_OBJECT *my_object;
-
-  TANK **tank, *ltank;
-  MISSILE *missile;
-  TELEPORT *teleport;
-  DECOR *decor;
-  BEAM *beam;
-  EXPLOSION *explosion;
-  FLOATTEXT *floattext;
-  static SATELLITE *satellite = NULL;
-
-  int bCount;
-  int z, zz, z4;
-  int objCount, count;
-  int AI_clock = 0;
-
-  global->computerPlayersOnly = FALSE;
-
-  tank = &global->currTank;
-
-  for (int doneCount = 0; doneCount < global->screenWidth; doneCount++)
-    env->done[doneCount] = 0;
-  // initSurface (global, env); // init surface[]
-
-  env->newRound ();
-  // set wall colour
-  switch (env->current_wallType)
-    {
-    case WALL_RUBBER:
-      env->wallColour = GREEN;
-      break;
-    case WALL_STEEL:
-      env->wallColour = RED;
-      break;
-    case WALL_SPRING:
-      env->wallColour = BLUE;
-      break;
-    case WALL_WRAP:
-      env->wallColour = YELLOW;
-      break;
-    }
-  if (env->dBoxedMode == 2.0)
-    {
-      if (rand() % 2)
-        global->bIsBoxed = true;
-      else
-        global->bIsBoxed = false;
-    }
-  else if (env->dBoxedMode == 1.0)
-    global->bIsBoxed = true;
-  else if (env->dBoxedMode == 0.0)
-    global->bIsBoxed = false;
-  // Set Max velocity
-  global->dMaxVelocity = (double)MAX_POWER * (100.0 / (double)global->frames_per_second) / 100.0;
-  if ((env->current_wallType == WALL_SPRING) && !global->bIsBoxed)
-    // In non-boxed the Spring Wall is allowed to have at least twice the normal velocity
-    global->dMaxVelocity *= 2.0;
-  if (global->bIsBoxed)
-    // In boxed Mode, there is four times the normal max velocity allowed (or it won't be fun!)
-    global->dMaxVelocity *= 4.0;
-  for (count = 0; count < global->numPlayers; count++)
-    global->players[count]->newRound ();
-
-  /* Unfortunately, ENVIRONMENT can't call uppon FLOATTEXT::newRound due to circular dependencies.
-     Thus we have to do that here: */
-
-  for (int objCount = 0; objCount < MAX_OBJECTS; objCount++)
-    {
-      if (env->objects[count] && (env->objects[count]->isSubClass(FLOATTEXT_CLASS)))
-        ((FLOATTEXT *)env->objects[count])->newRound();
-    }
-
-  buystuff (global, env);
-  if (global->close_button_pressed)
-    {
-      global->wr_lock_command();
-      global->command = GLOBAL_COMMAND_QUIT;
-      global->unlock_command();
-      return;
-    }
-
-  for (count = 0; count < global->numPlayers; count++)
-    global->players[count]->exitShop ();
-
-  set_level_settings (global, env);
-
-  for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-    {
-      ltank->newRound ();
-      if ((int)ltank->player->type == HUMAN_PLAYER)
-        {
-          humanPlayers++;
-        }
-    }
-  if (!humanPlayers)
-    {
-      global->computerPlayersOnly = TRUE;
-      //if ((int)global->skipComputerPlay >= SKIP_AUTOPLAY)
-      //	skippingComputerPlay = TRUE;
-    }
-  lock_cclock();
-  cclock = lx = ly = tanksfall = 0;
-  unlock_cclock();
-  env->stage = STAGE_AIM;
-  tlt = global->updateCount = dclock = global->stopwindow = 0;
-  env->realm = env->am = 0;
-  ca = 0;
-  lb = env->mouseclock = env->pclock = 0;
-  ord = 0;
-
-  *tank = env->order[0];
-  for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-    {
-      ltank->flashdamage = 0;
-      ltank->boost_up_shield ();
-    }
-  winner = tanklife = -1;
-  fi = global->updateMenu = 1;
-  global->window.x = 0;
-  global->window.y = 0;
-  global->window.w = (global->screenWidth-1);
-  global->window.h = (global->screenHeight-1);
-  bCount = 0;
-  if ((int)env->windstrength != 0)
-    env->wind = (float)(rand () % (int)env->windstrength) - (env->windstrength / 2);
-  else
-    env->wind = 0;
-  env->lastwind = env->wind;
-  if ( (env->satellite) && (! satellite) )
-    satellite = new SATELLITE(global, env);
-  if (satellite)
-    satellite->Init();
-
-  global->iHumanLessRounds = -1;
-#ifdef DEBUG_AIM_SHOW
-  global->bASD = false;
+#ifdef NETWORK
+	client_socket = Setup_Client_Socket(env.server_name, env.server_port);
+	if (client_socket >= 0) {
+		bool keep_playing = true;
+		cout << "Ready to play networked" << endl;
+
+		while (keep_playing)
+			keep_playing = Game_Client(client_socket);
+
+		Clean_Up_Client_Socket(client_socket);
+	} else
+		cerr << "ERROR: Unable to connect to server " << env.server_name
+		     << ", port " << env.server_port << endl;
+#else
+		char noNetworkMsg[200] = { 0 };
+		snprintf(noNetworkMsg, 199,
+		         "This version of Atanks is not compiled to"
+		         " handle network games.");
+		errorMessage = noNetworkMsg;
+		errorX       = env.halfWidth - text_length(font, errorMessage) / 2;
+		errorY       = env.menuBeginY + 15;
+		cerr << "ERROR: " << noNetworkMsg << endl;
 #endif
 
-  global->background_music = global->Load_Background_Music();
-  if (global->background_music)
-     play_sample((SAMPLE *) global->background_music, 255, 128, 1000, TRUE);
-
-  while (1)
-    {
-      LINUX_SLEEP;
-      if (global->close_button_pressed)
-        {
-          global->wr_lock_command();
-          global->command = GLOBAL_COMMAND_QUIT;
-          global->wr_unlock_command();
-          return;
-        }
-
-      while (get_cclock() > 0 || skippingComputerPlay)
-        {
-          lock_cclock();
-          cclock--;
-          unlock_cclock();
-          if (!lb && mouse_b & 1)
-            env->mouseclock = 0;
-          lb = (mouse_b & 1) ? 1 : 0;
-          bCount += 360/32;
-          z4 = 0;
-          anyExploding    = 0;
-          anyTeleporting  = 0;
-          anyLaserFiring  = 0;
-
-          env->am = 0;
-          objCount = 0;
-          my_object = env->objects[objCount];
-          // keep track of how long we have been skipping AI
-          if (skippingComputerPlay)
-          {
-             // advance clock
-             if ( global->Check_Time_Changed() )
-                AI_clock++;
-             if (AI_clock > MAX_AI_TIME)
-             {
-                 int player_index = 0;
-                 // kill all remaining tanks
-                 while (player_index < global->numPlayers)
-                 {
-                    if ( ( global->players[player_index] ) &&
-                         ( global->players[player_index]->tank ) )
-                       global->players[player_index]->tank->l = 0;
-                    player_index++;
-                 }
-             }
-          }       // end of AI clock code
-
-          while (objCount < MAX_OBJECTS) 
-          {
-            if (my_object)
-            {
-            my_class = my_object->getClass();
-            //for (objCount = 0; (decor = (DECOR*)env->getNextOfClass (DECOR_CLASS, &objCount)) && decor; objCount++)
-            if (my_class == DECOR_CLASS)
-            {
-              decor = (DECOR *) my_object;
-              decor->applyPhysics ();
-              if (decor->destroy)
-                {
-                  decor->requireUpdate ();
-                  decor->update ();
-                  delete decor;
-                }
-            }
-          // for (objCount = 0; (explosion = (EXPLOSION*)env->getNextOfClass (EXPLOSION_CLASS, &objCount)) && explosion; objCount++)
-            else if (my_class == EXPLOSION_CLASS)
-            {
-              explosion = (EXPLOSION *) my_object;
-              if (explosion->bIsWeaponExplosion)
-                anyExploding++;
-              explosion->explode ();
-              explosion->applyPhysics ();
-              if (explosion->destroy)
-                {
-                  explosion->requireUpdate ();
-                  explosion->update ();
-                  delete explosion;
-                  anyExploding--;
-                }
-            }
-
-          // for (objCount = 0; (teleport = (TELEPORT*)env->getNextOfClass (TELEPORT_CLASS, &objCount)) && teleport; objCount++)
-            else if (my_class == TELEPORT_CLASS)
-            {
-              teleport = (TELEPORT *) my_object;
-              anyTeleporting++;
-              teleport->applyPhysics ();
-              if (teleport->destroy)
-                {
-                  teleport->requireUpdate ();
-                  teleport->update ();
-                  delete teleport;
-                  anyTeleporting--; // It's done!
-                  tanksfall = 1;
-                }
-            }
-          // env->am = 0;
-          // for (objCount = 0; (missile = (MISSILE*)env->getNextOfClass (MISSILE_CLASS, &objCount)) && missile; objCount++)
-            else if (my_class == MISSILE_CLASS)
-            {
-              TANK *shooting_tank = NULL;
-              BEAM *defense_beam = NULL;
-              int angle_to_fire;
-
-              missile = (MISSILE *) my_object;
-              env->am++;
-              missile->hitSomething = 0;
-              missile->applyPhysics ();
-              missile->triggerTest ();
-              shooting_tank = missile->Check_SDI(global);
-              if (shooting_tank)
-              {
-                  (shooting_tank->x < missile->x) ? angle_to_fire = 135 : angle_to_fire = 225;
-                  defense_beam = new BEAM(global, env, shooting_tank->x, shooting_tank->y - 10, angle_to_fire, SML_LAZER);
-                  missile->trigger();
-              }
-              if (missile->destroy)
-                {
-                  missile->requireUpdate ();
-                  missile->update ();
-                  delete missile;
-                  tanksfall = 1;
-                }
-            }
-          // for (objCount = 0; (beam = (BEAM*)env->getNextOfClass (BEAM_CLASS, &objCount)) && beam; objCount++)
-            else if (my_class == BEAM_CLASS)
-            {
-              beam = (BEAM *) my_object;
-              // As bots should not target while a laser is shot:
-              anyLaserFiring ++;
-              beam->applyPhysics ();
-              if (beam->destroy)
-                {
-                  beam->requireUpdate ();
-                  beam->update ();
-                  delete beam;
-                  anyLaserFiring--; // It's done!
-                  tanksfall = 1;
-                }
-            }
-          // for (objCount = 0; (floattext = (FLOATTEXT*)env->getNextOfClass (FLOATTEXT_CLASS, &objCount)) && floattext; objCount++)
-            else if (my_class == FLOATTEXT_CLASS)
-            {
-              floattext = (FLOATTEXT *) my_object;
-              floattext->applyPhysics ();
-              if (floattext->destroy)
-                {
-                  floattext->requireUpdate();
-                  floattext->update();
-                  delete floattext;
-                  env->make_fullUpdate(); // ...kill remaining texts!
-                }
-            }
-            }        // end of if we have an object
-            objCount++;
-            my_object = env->objects[objCount];
-          }   // end of going through virtual objects
-
-
-          #ifdef NETWORK
-          for (int counter = 0; counter < global->numPlayers; counter++)
-          {
-               if (global->players[counter]->type == NETWORK_CLIENT)
-               {
-                  global->players[counter]->Get_Network_Command();
-                  global->players[counter]->Execute_Network_Command(FALSE);
-               }
-          }
-          #endif
-
-          if (satellite)
-            satellite->Move();
-
-          if (!anyExploding)
-            {
-              doNaturals (global, env);
-              if (satellite)
-                satellite->Shoot();
-            }
-
-          allDone = slideLand (global, env);
-          if (tanksfall && (env->stage != STAGE_ENDGAME))
-            {
-              for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank;
-                   count--, objCount++)
-                {
-                  ltank->pen = 0;
-                  ltank->applyPhysics (global);
-                  if (ltank->l <= 0 && !anyExploding)
-                    {
-                      ltank->explode ();
-                      if (ltank->creditTo)
-                        {
-                          if (ltank->player != ltank->creditTo)  	//enemy destroyed
-                            {
-                              ltank->creditTo->money += (int)global->scoreUnitDestroyBonus;
-                            }
-                          else  	//self destroy - ugh foolish one :))
-                            {
-                              ltank->creditTo->money -= (int)global->scoreUnitSelfDestroy;
-                              if (ltank->creditTo->money < 0)
-                                ltank->creditTo->money = 0;
-                            }
-                          ltank->creditTo = NULL;
-                        }
-                      if (ltank->destroy)
-                        {
-                          if ((int)ltank->player->type == HUMAN_PLAYER)
-                            humanPlayers--;
-                          
-                          ltank->Destroy();
-                          delete(ltank);
-
-                          ltank = NULL; // should not be used anymore, setting NULL for later IF's to checks
-                          if ((!skippingComputerPlay) || (global->numTanks <= 1))
-                            env->make_fullUpdate();
-                          if (!humanPlayers)
-                            {
-                              int iMostIntelligentPlayer = 0;
-                              int iNumPlayers            = 0;
-
-                              for (int i = 0; i < global->numPlayers; i++)
-                                {
-                                  int iType = 0;
-
-                                  if (global->players[i]->tank)
-                                    {
-                                      if (global->players[i]->tank->l > 0)
-                                        {
-                                          iType = (int) global->players[i]->type;
-                                          iNumPlayers++;
-                                        }
-                                    }
-                                  if (iType > iMostIntelligentPlayer)
-                                    iMostIntelligentPlayer = iType;
-                                }
-                              // If the most intelligent player is more stupid than deadly, raise them all!
-                              if	( (iMostIntelligentPlayer < (int) DEADLY_PLAYER)
-                                   && (iMostIntelligentPlayer >= (int) USELESS_PLAYER)
-                                   && (iNumPlayers > 1))
-                                for (int i = 0; i < global->numPlayers; i++)
-                                  {
-                                    if (global->players[i]->tank)
-                                      {
-                                        if (global->players[i]->tank->l > 0)
-                                          global->players[i]->setComputerValues ( (int) DEADLY_PLAYER - iMostIntelligentPlayer);
-                                      }
-                                  }
-
-#ifdef DEBUG
-                              if ((iMostIntelligentPlayer >= (int)USELESS_PLAYER) && (iNumPlayers > 1))
-                                {
-                                  cout << endl << "===========================================================" << endl;
-                                  cout << "Round without human players!" << endl;
-                                  cout << "Most intelligent player has Skill level " << iMostIntelligentPlayer << endl;
-                                  cout << "Raised all players by " << ((int)DEADLY_PLAYER - iMostIntelligentPlayer) << " Skill Level(s)" << endl;
-                                  cout << "===========================================================" << endl << endl;
-                                }
-#endif //DEBUG
-                              // Initialize iHumanLessRounds if not done already
-                              if (global->iHumanLessRounds < 0)
-                                global->iHumanLessRounds = iNumPlayers * 16;
-                            }
-                          if (!*tank && global->numTanks)
-                            {
-                              *tank = nextturn (global, env, skippingComputerPlay);
-                              while (! *tank)
-                                *tank = nextturn (global, env, skippingComputerPlay);
-                              (*tank)->fs = 0;
-                              nextTankSelected = true;
-                            }
-                          team_won = Team_Won(global);
-                          if ( (global->numTanks <= 1) || ( team_won ) )
-                            {
-                              skippingComputerPlay = FALSE;
-                              lock_cclock();
-                              cclock = 0;
-                              unlock_cclock();
-                              env->stage = STAGE_ENDGAME;
-                              global->currTank = NULL;
-                              fi = 1;
-                              global->window.x = 0;
-                              global->window.y = 0;
-                              global->window.w = (global->screenWidth-1);
-                              global->window.h = (global->screenHeight-1);
-                              winner = -2;
-                              if (team_won)
-                                winner = team_won;
-                              else if (global->numTanks > 0)
-                                {
-                                  for (z = 0; z < global->numPlayers; z++)
-                                    {
-                                      if (global->players[z]->tank)
-                                        winner = z;
-                                    }
-                                }
-
-                              for (objCount = 0; (floattext = (FLOATTEXT*)env->getNextOfClass (FLOATTEXT_CLASS, &objCount)) && floattext; objCount++)
-                                {
-                                  floattext->newRound();
-                                }
-
-                              bCount = 0;
-                              global->updateMenu = 1;
-                              for (z = 0; z < global->numPlayers; z++)
-                                global->players[z]->played++;
-                            }
-                        }
-                    }
-
-                  // adjust chess style clock (only if tank wasn't destroyed)
-                  if ( ( global->max_fire_time > 0.0 ) &&
-                       ( ltank ) &&
-                       ( ltank->player->type == HUMAN_PLAYER ) &&
-                       (! env->stage ) )
-                    {
-                      if ( global->Check_Time_Changed() )
-                        {
-                          int ran_out_of_time;
-                          ran_out_of_time = ltank->player->Reduce_Time_Clock();
-                          if (ran_out_of_time && !nextTankSelected)
-                            {
-                              ltank->player->skip_me = true;
-                              *tank = nextturn (global, env, skippingComputerPlay);
-                              while (! *tank)
-                                *tank = nextturn (global, env, skippingComputerPlay);
-                              (*tank)->fs = 0;
-                              nextTankSelected = true;
-                            }
-                          global->updateMenu = 1;
-                        }
-                    }
-
-                  if ( ( ltank ) && ( ltank->fire_another_shot ) )
-                  {
-                      if (! (ltank->fire_another_shot % VOLLY_DELAY))
-                        ltank->activateCurrentSelection();
-                      ltank->fire_another_shot--;
-                      if (! ltank->fire_another_shot)
-                         env->stage = 0;
-                  }
-
-                }
-            }
-          if (env->stage == 1)
-            {
-              if ((env->am == 0) && allDone && !anyExploding)
-                {
-                  tanksfall = 0;
-                  env->stage = 2;
-                }
-            }
-          if (env->stage == 2)
-            {
-              zz = 0;
-
-              for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; count--, objCount++)
-                {
-                  zz += ltank->applyPhysics (global);
-                }
-              if (zz == global->numTanks)
-                {
-                  tanksfall = 1;
-                }
-              zz = 0;
-              if (tanksfall)
-                {
-                  for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-                    {
-                      ltank->pen = 0;
-                      if (ltank->l > 0)
-                        zz++;
-                    }
-                }
-              if (zz == global->numTanks)
-                {
-                  if (((int)global->skipComputerPlay > SKIP_NONE) &&
-                      (!humanPlayers) && (!global->computerPlayersOnly))
-                    {
-                        skippingComputerPlay = TRUE;
-                        #ifdef NETWORK
-                        int index = 0, network_clients = 0;
-                        while ( (index < global->numPlayers) && (! network_clients) )
-                        {
-                            if (global->players[index]->type == NETWORK_CLIENT)
-                               network_clients++;
-                            else
-                               index++;
-                        }
-                        if (network_clients)
-                          skippingComputerPlay = FALSE;
-                        #endif
-                       if (skippingComputerPlay)
-                       {
-                         draw_sprite (env->db, (BITMAP *) global->misc[1], global->halfWidth - 120, global->halfHeight + 155);
-                         textout_centre_ex (env->db, font, global->ingame->complete_text[51], global->halfWidth, global->halfHeight + 160, WHITE, -1);
-                         draw_sprite (screen, (BITMAP *) global->misc[1], global->halfWidth - 120, global->halfHeight + 155);
-                         textout_centre_ex (screen, font, global->ingame->complete_text[51], global->halfWidth, global->halfHeight + 160, WHITE, -1);
-                       }
-                    }
-                    
-                  env->stage = STAGE_AIM;
-                  winner = -2;
-                  for (z = 0; z < global->numPlayers; z++)
-                    {
-                      if (global->players[z]->tank && winner >= 0)
-                        winner = -1;
-                      if (global->players[z]->tank && winner == -2)
-                        winner = z;
-                    }
-                  if (winner >= 0)
-                    {
-                      global->players[winner]->score++;
-                    }
-                  if (winner == -1)
-                    {
-                      if ((!nextTankSelected || !tank) && global->numTanks)
-                        {
-                          *tank = nextturn (global, env, skippingComputerPlay);
-                          while (! *tank)
-                            *tank = nextturn (global, env, skippingComputerPlay);
-                          (*tank)->fs = 0;
-                        }
-                      nextTankSelected = false;
-                    }
-                  if ((!humanPlayers) && (!global->computerPlayersOnly))
-                    {
-                      global->iHumanLessRounds--;
-#ifdef DEBUG
-                      cout << endl << global->iHumanLessRounds << " Rounds left for the bots to play... " << endl;
-#endif // DEBUG
-                      if ((!global->iHumanLessRounds) && (winner < 1))
-                        {
-#ifdef DEBUG
-                          cout << endl << "=======================" << endl;
-                          cout << "Bots have FINISHED!... " << endl;
-                          cout << "=======================" << endl << endl;
-#endif // DEBUG
-                          global->iHumanLessRounds = -1;
-                          team_won = NO_WIN; // will be determined later
-                          skippingComputerPlay = FALSE;
-                          lock_cclock();
-                          cclock = 0;
-                          unlock_cclock();
-                          winner = -2;
-                          if (global->numTanks > 0)
-                            {
-                              // The most healthy player wins
-                              int iMaxHealth = 1;
-                              int iHealth = 0;
-                              for (z = 0; z < global->numPlayers; z++)
-                                {
-#ifdef DEBUG
-                                  cout << z << ".: \"" << global->players[z]->getName() << "\" ";
-#endif // DEBUG
-                                  if (global->players[z]->tank)
-                                    {
-                                      iHealth = global->players[z]->tank->l + global->players[z]->tank->sh;
-                                      if (iHealth > iMaxHealth)
-                                        {
-#ifdef DEBUG
-                                          cout << "has most health! (" << iHealth << ")" << endl;
-#endif // DEBUG
-                                          iMaxHealth = iHealth;
-                                          winner = z;
-                                        }
-                                      else if (iHealth == iMaxHealth)
-                                        winner = -2; // Equal Health == draw!
-#ifdef DEBUG
-                                      else
-                                        cout << "is too weak! (" << iHealth << " max: " << iMaxHealth << ")" << endl;
-#endif // DEBUG
-                                    }
-#ifdef DEBUG
-                                  else
-                                    cout << "has no tank!" << endl;
-#endif //
-                                }
-#ifdef DEBUG
-                              cout << "winner is player " << winner << " - \"";
-                              if (winner > 0)
-                                cout << global->players[winner]->getName();
-                              else
-                                cout << "draw";
-                              cout << "\"" << endl;
-#endif // DEBUG
-                              if (winner > -1)
-                                {
-                                  if (global->players[winner]->team == TEAM_JEDI)
-                                    {
-                                      team_won = JEDI_WIN;
-                                      winner = JEDI_WIN;
-                                    }
-                                  if (global->players[winner]->team == TEAM_SITH)
-                                    {
-                                      team_won = SITH_WIN;
-                                      winner = SITH_WIN;
-                                    }
-                                }
-                            }
-#ifdef DEBUG
-                          cout << "Final Decision:" << endl;
-                          if (winner > -1)
-                            {
-                              cout << "\"";
-                              if (winner < 10)
-                                cout << global->players[winner]->getName();
-                              if (winner == JEDI_WIN)
-                                cout << "Team Jedi";
-                              if (winner == SITH_WIN)
-                                cout << "Team Sith";
-                              cout << "\" has won!" << endl;
-                            }
-                          else
-                            cout << "Round Draw!" << endl;
-#endif // DEBUG
-                        }
-                    }
-                  if (winner >= 0 || winner == -2)
-                    {
-                      env->stage = STAGE_ENDGAME;
-                      global->currTank = NULL;
-                      fi = 1;
-                      global->window.x = 0;
-                      global->window.y = 0;
-                      global->window.w = (global->screenWidth-1);
-                      global->window.h = (global->screenHeight-1);
-                    }
-                  bCount = 0;
-                  global->updateMenu = 1;
-                }
-            }
-          dclock++;
-          if (dclock > 2)
-            {
-              dclock = 0;
-              for (objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-                {
-                  if (ltank->flashdamage)
-                    {
-                      if (ltank->flashdamage > 25 || ltank->l < 1)
-                        {
-                          ltank->damage = 0;
-                          ltank->flashdamage = 0;
-                          ltank->requireUpdate ();
-                        }
-                    }
-                }
-            }
-          env->pclock++;
-          if (env->pclock > 10)
-            env->pclock = 0;
-          if (*tank && !anyTeleporting && !anyLaserFiring)
-            {
-              // if ((*tank)->player->controlTank() == -1)
-              int status = (*tank)->player->controlTank();
-              if (status == -1)
-                return;
-              else if ( (status == -2) && (!humanPlayers) )
-              {
-                skippingComputerPlay = TRUE;
-              }
-            }
-          else if (global->computerPlayersOnly &&
-                   ((int)global->skipComputerPlay >= SKIP_HUMANS_DEAD))
-            {
-              if (env->stage == STAGE_ENDGAME)
-                return;
-            }
-          else if ((keypressed () || mouse_b) && !fi)
-            {
-              if (keypressed ())
-                k = readkey ();
-              else
-                k = 0;
-              if ((env->stage == STAGE_ENDGAME) && (roundEndCount >= WAIT_AT_END_OF_ROUND) &&
-                  (mouse_b || k >> 8 == KEY_ENTER || k >> 8 == KEY_ESC || k >> 8 == KEY_SPACE))
-                return;
-            }
-          env->mouseclock++;
-          if (env->mouseclock > 10)
-            env->mouseclock = 0;
-        }
-      frames++;
-      global->stopwindow = 1;
-      env->make_update (mouse_x, mouse_y, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      env->make_update (lx, ly, ((BITMAP *) (global->misc[0]))->w, ((BITMAP *) (global->misc[0]))->h);
-      global->stopwindow = 0;
-      lx = mouse_x;
-      ly = mouse_y;
-      set_clip_rect (env->db, 0, 0, (global->screenWidth-1), (global->screenHeight-1));
-      if (! global->os_mouse) show_mouse (NULL);
-      if (global->updateMenu)
-        {
-          set_clip_rect (env->db, 0, 0, (global->screenWidth-1), MENUHEIGHT - 1);
-          drawTopBar (global, env, env->db);
-        }
-      set_clip_rect (env->db, 0, MENUHEIGHT, (global->screenWidth-1), (global->screenHeight-1));
-      if (fi)
-        {
-          blit (env->sky, env->db, global->window.x, global->window.y - MENUHEIGHT, global->window.x, global->window.y, (global->window.w - global->window.x) + 1, (global->window.h - global->window.y) + 1);
-          masked_blit (env->terrain, env->db, global->window.x, global->window.y, global->window.x, global->window.y, (global->window.w - global->window.x) + 1, (global->window.h - global->window.y) + 2);
-        }
-      else
-        {
-          env->replaceCanvas ();
-        }
-
-      for (objCount = 0, count = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; count++, objCount++)
-        {
-          if (env->stage < STAGE_ENDGAME)
-            {
-              if (*tank == ltank)
-                {
-                  ltank->draw (env->db, (int)(global->slope[bCount % 360][0] * 4));
-                  ltank->requireUpdate ();
-                }
-              else
-                {
-                  ltank->draw (env->db, 0);
-                }
-              ltank->update ();
-            }
-          ltank->framelyAccounting ();
-        }
-
-      objCount = 0;
-      my_object = env->objects[objCount];
-      while (objCount < MAX_OBJECTS)
-      {
-          if (my_object)
-          {
-          my_class = my_object->getClass();
-      // for (objCount = 0; (missile = (MISSILE*)env->getNextOfClass (MISSILE_CLASS, &objCount)) && missile; objCount++)
-        if (my_class == MISSILE_CLASS)
-        {
-          missile = (MISSILE *) my_object;
-          missile->draw (env->db);
-          missile->update ();
-        }
-      // for (objCount = 0; (beam = (BEAM*)env->getNextOfClass (BEAM_CLASS, &objCount)) && beam; objCount++)
-        else if (my_class == BEAM_CLASS)
-        {
-          beam = (BEAM *) my_object;
-          beam->draw (env->db);
-          beam->update ();
-        }
-      // for (objCount = 0; (explosion = (EXPLOSION*)env->getNextOfClass (EXPLOSION_CLASS, &objCount)) && explosion; objCount++)
-        else if (my_class == EXPLOSION_CLASS)
-        {
-          explosion = (EXPLOSION *) my_object;
-          explosion->draw (env->db);
-          explosion->update ();
-        }
-      // for (objCount = 0; (teleport = (TELEPORT*)env->getNextOfClass (TELEPORT_CLASS, &objCount)) && teleport; objCount++)
-        else if (my_class == TELEPORT_CLASS)
-        {
-          teleport = (TELEPORT *) my_object;
-          if (teleport->object)
-            teleport->draw (env->db);
-          teleport->update ();
-        }
-      // for (objCount = 0; (decor = (DECOR*)env->getNextOfClass (DECOR_CLASS, &objCount)) && decor; objCount++)
-        else if (my_class == DECOR_CLASS)
-        {
-          decor = (DECOR *) my_object;
-          decor->draw (env->db);
-          decor->update ();
-        }
-      // for (objCount = 0; (floattext = (FLOATTEXT*)env->getNextOfClass (FLOATTEXT_CLASS, &objCount)) && floattext; objCount++)
-        else if (my_class == FLOATTEXT_CLASS)
-        {
-          floattext = (FLOATTEXT *) my_object;
-          floattext->draw (env->db);
-          floattext->requireUpdate ();
-          floattext->update ();
-        }
-        }        // end of if we have an object
-        objCount++;
-        my_object = env->objects[objCount];
-      }      // end of going through objects
-
-      if (satellite)
-        satellite->Draw(env->db);
-
-      if (env->stage == STAGE_ENDGAME)
-        {
-          if (roundEndCount < WAIT_AT_END_OF_ROUND + 1)
-            roundEndCount++;
-          if (roundEndCount >= WAIT_AT_END_OF_ROUND)
-            {
-              // check to see if the winner is still alive
-              int tank_index = 0;
-              int alive = false;
-              while ( (! alive) && (tank_index < global->numPlayers) )
-                {
-                  if ( ( global->players[tank_index]->tank )
-                       &&( global->players[tank_index]->tank->l > 0) )
-                  {
-                    alive = true;
-                  }
-                  tank_index++;
-                }
-
-                // moving this below so dead players lose credit
-//              if (! alive)
-//              {
-//                 winner = -2;
-//              }
-
-              if (! alive && bWinnerIsCredited)
-                {
-                  // The score needs to be reduced, of course:
-                  int count;
-                  int iTeamCount = 0;
-                  int iTeamBonus = 0;
-                  if (winner == JEDI_WIN)   // de-credit jedi team
-                    {
-                      for (count = 0; count < global->numPlayers; count++)
-                        {
-                          if (global->players[count]->team == TEAM_JEDI)
-                            {
-                              global->players[count]->score--;
-                              global->players[count]->won--;
-                              iTeamCount++;
-                            }
-                        }
-                    }
-                  else if (winner == SITH_WIN) // de-credit sith team
-                    {
-                      for (count = 0; count < global->numPlayers; count++)
-                        {
-                          if (global->players[count]->team == TEAM_SITH)
-                            {
-                              global->players[count]->score--;
-                              global->players[count]->won--;
-                              iTeamCount++;
-                            }
-                        }
-
-                    }
-                  else if (winner >= 0)    // de-credit the (ex-)winner
-                    {
-                      global->players[winner]->score--;
-                      global->players[winner]->won--;
-                      global->players[winner]->money -= (int)global->scoreRoundWinBonus;
-                    }
-                  // If it's a team, take away their money now!
-                  if (iTeamCount)
-                    {
-                      iTeamBonus = (int)(global->scoreRoundWinBonus / iTeamCount);
-                      for (count = 0; count < global->numPlayers; count++)
-                        if (	((winner==JEDI_WIN) && (global->players[count]->team == TEAM_JEDI))
-                             ||((winner==SITH_WIN) && (global->players[count]->team == TEAM_SITH))	)
-                          global->players[count]->money -= iTeamBonus;
-                    }
-                  winner = -2;
-                  bWinnerIsCredited = false; // Or it will be substracted on every loop...
-                }
-
-              if (! alive)
-              {
-                 winner = -2;
-              }
-
-
-              // if we have a winner, give them credit
-              if (alive && (winner >= 0) && (roundEndCount == WAIT_AT_END_OF_ROUND) && !bWinnerIsCredited)
-                {
-                  int count;
-                  int iTeamCount = 0;
-                  int iTeamBonus = 0;
-                  if (winner == JEDI_WIN)   // credit jedi team
-                    {
-                      for (count = 0; count < global->numPlayers; count++)
-                        {
-                          if (global->players[count]->team == TEAM_JEDI)
-                            {
-                              global->players[count]->score++;
-                              global->players[count]->won++;
-                              iTeamCount++;
-                            }
-                        }
-                    }
-                  else if (winner == SITH_WIN) // credit sith team
-                    {
-                      for (count = 0; count < global->numPlayers; count++)
-                        {
-                          if (global->players[count]->team == TEAM_SITH)
-                            {
-                              global->players[count]->score++;
-                              global->players[count]->won++;
-                              iTeamCount++;
-                            }
-                        }
-
-                    }
-                  else    // credit the winner
-                    {
-                      global->players[winner]->score++;
-                      global->players[winner]->won++;
-                      global->players[winner]->money += (int)global->scoreRoundWinBonus;
-                    }
-                  // If it's a team, do give them their money now!
-                  if (iTeamCount)
-                    {
-                      iTeamBonus = (int)(global->scoreRoundWinBonus / iTeamCount);
-                      for (count = 0; count < global->numPlayers; count++)
-                        if (	((winner==JEDI_WIN) && (global->players[count]->team == TEAM_JEDI))
-                             ||((winner==SITH_WIN) && (global->players[count]->team == TEAM_SITH))	)
-                          global->players[count]->money += iTeamBonus;
-                    }
-                  bWinnerIsCredited = true;
-                }
-
-              if ( roundEndCount >= WAIT_AT_END_OF_ROUND )
-                showRoundEndScoresAt (global, env, env->db, global->screenWidth/2, global->screenHeight/2, winner);
-                 // when we get here and it is a demo, we should bail out
-                 if (global->demo_mode)
-                    return;
-            }
-        }
-      // This four values are used to reduce access to global and increase readability (Easier to debug this way!)
-      int iLeft = 0;
-      int iRight = global->screenWidth - 1;
-      int iTop = MENUHEIGHT;
-      int iBottom = global->screenHeight - 1;
-      set_clip_rect (env->db, 0, 0, iRight, iBottom);
-      vline(env->db, iLeft, iTop, iBottom, env->wallColour);	// Left edge
-      vline(env->db, iRight, iTop, iBottom, env->wallColour);	// right edge
-      hline(env->db, iLeft, iBottom, iRight, env->wallColour);// bottom edge
-      if (global->bIsBoxed)
-        hline(env->db, iLeft, iTop, iRight, env->wallColour);// top edge
-      if (! global->os_mouse) show_mouse (env->db);
-      if (fi)
-        {
-          while (keypressed ())
-            {
-              readkey ();
-            }
-          fi = 0;
-
-          // if (env->fog) {
-          //	clear_to_color (screen, makecol (128,128,128));
-          // } else {
-          quickChange (global, env->db);
-          // }
-        }
-      else
-        {
-          env->do_updates ();
-          global->window.x = global->screenWidth;
-          global->window.y = global->screenHeight;
-          global->window.w = -1;
-          global->window.h = -1;
-        }
-#ifdef DEBUG_AIM_SHOW
-      if (!global->bASD)
-        global->bASD = true; // Now it is allowed to be true
-#endif
-    }
 }
-#endif // old gameloop
 
 
-void print_text_help()
+static void print_text_help()
 {
-  cout	<< "-h\tThis screen\n"
-       << "-fs\tFull screen\n"
-       << "--windowed\tRun in a window\n"
-       << "-w <width> or --width <width>\tSpecify the screen width in pixels\n"
-       << "-t <height> --tall <height>\tSpecify the screen height in pixels\n"
-       << "\tAdjust the screen size at your own risk (default is 800x600)\n"
-       << "-d <depth> or --depth <depth>\tCurrently either 16 or 32\n"
-       << "--datadir <data directory>\t Path to the data directory\n"
-       << "-c <config directory>\t Path to config and saved game directory\n"
-       << "--noconfig\t Do not load game settings from the config file.\n"
-       << "--nosound\t Disable sound\n"
-       << "--noname\t Do not show player name above tank\n"
-       << "--nonetwork\t Do not allow the game to accept network connection.\n"
-       << "--nobackground\t Do not display the green menu background.\n"
-       << "--nothread\t Do not use threads to perform background tasks.\n"
-       << "--thread\t Do use threads to perform background tasks.\n";
+	cout << "-h  --help           Show this help screen\n"
+	     << "-fs                  Full screen\n"
+	     << "    --windowed       Run in a window\n"
+	     << "-w  --width <width>  Specify the screen width in pixels\n"
+	     << "-t  --tall  <height> Specify the screen height in pixels\n"
+	     << "                     Adjust the screen size at your own risk\n"
+	     << "                     (default is 800x600)\n"
+	     << "-d  --depth <depth>  Colour depths, currently either 16 or 32 bit\n"
+	     << "    --datadir <path> Path to the data directory\n"
+	     << "-c <path>            Path to config and saved game directory\n"
+	     << "    --noconfig       Do not load game settings.\n"
+	     << "    --nosound        Disable sound\n"
+	     << "    --noname         Do not show player name above tank\n"
+	     << "    --nonetwork      Disable network connections.\n"
+	     << "    --nobackground   Do not display the green menu background."
+	     << endl;
 }
 
-void print_text_initmsg()
-{
-  printf ( "Atomic Tanks Version %s (-h for help)\n", VERSION);
-  printf ( "Authors: \tTom Hudson (rewrite, additions, improvements)\n");
-  printf ( "\t\tStevante Software (original design)\n");
-  printf ( "\t\tKota543 Software (fixes and updates)\n");
-  printf ( "\t\tJesse Smith (additions, fixes and updates)\n");
-  printf ( "\t\tSven Eden (ai rewrite, additions, fixes and updates)\n\n");
-
-  // putchar ('\n');
-}
 
-void endgame_cleanup (GLOBALDATA *global, ENVIRONMENT *env)
+static void print_text_initmsg()
 {
-  while (global->numPlayers > 0)
-    {
-      if (global->players[0]->tank)
-        delete(global->players[0]->tank);
-      // make sure networked clients say good-bye and return to old AI level
-      if (global->players[0]->type >= NETWORK_CLIENT)
-         global->players[0]->type = global->players[0]->previous_type;
-
-      global->players[0]->tank = NULL;
-      global->removePlayer(global->players[0]);
-    }
-  for (int objCount = 0; objCount < MAX_OBJECTS; objCount++)
-    {
-      if (env->objects[objCount])
-        {
-          delete(env->objects[objCount]);
-          env->objects[objCount] = NULL;
-        }
-    }
+	cout << "Atomic Tanks Version " << VERSION << " (-h for help)\n"
+	     << "Authors: Tom Hudson        (rewrite, additions, improvements)\n"
+	     << "         Stevante Software (original design)\n"
+	     << "         Kota543 Software  (fixes and updates)\n"
+	     << "         Jesse Smith       (additions, fixes and updates)\n"
+		 << "         Sven Eden         (ai rewrite, additions, fixes and updates)\n\n"
+		 << endl;
 }
 
 
@@ -5039,1209 +1406,193 @@ the config file name.
 The function returns TRUE on success and FALSE on failure.
 -- Jesse
 */
-int Save_Game_Settings_Text(GLOBALDATA *global, ENVIRONMENT *env, char *text_file)
+static bool Save_Game_Settings(const char* path)
 {
-  FILE *my_file;
+	FILE* file = fopen(path, "w");
+	if (!file) {
+		perror ( "Error trying to open text file for writing.\n");
+		return false;
+	}
 
-  my_file = fopen(text_file, (char *)"w");
-  if (! my_file)
-  {
-      perror ( "Error trying to open text file for writing.\n");
-      return FALSE;
-    }
+	env.save_to_file (file);
 
-  global->saveToFile_Text (my_file);
-  env->saveToFile_Text (my_file);
-  savePlayers_Text (global, my_file);
-  fclose (my_file);
-  return TRUE;
-}
+	for (int32_t i = 0; i < env.numPermanentPlayers; ++i)
+		env.allPlayers[i]->save_to_file(file);
 
-/*
-This function detects changes to the global settings (mouse and sound)
-and, if a change has happened, makes the required changes to the
-game environment.
-The function returns TRUE.
--- Jesse
-*/
-int Change_Settings(double old_mouse, double old_sound, double new_mouse, double new_sound, void *mouse_image)
-{
-  BITMAP *my_mouse_image = (BITMAP *) mouse_image;
-
-  // first, check for a change in the sound settings
-  if (old_sound != new_sound)
-    {
-      if (new_sound > 0.0)    // we turned ON sound
-      {
-            if (detect_digi_driver(DIGI_AUTODETECT))
-            {
-                if (install_sound (DIGI_AUTODETECT, MIDI_NONE, NULL) < 0)
-                    fprintf (stderr, "install_sound: failed turning on sound\n");
-            }
-            else
-                fprintf (stderr, "detect_digi_driver found no sound device\n");
-
-//          if (install_sound (DIGI_AUTODETECT, MIDI_NONE, NULL) < 0)
-//            {
-//              fprintf (stderr, "install_sound: %s", allegro_error);
-//            }
-      }
-      else if (new_sound == 0.0)  // we turned OFF sound
-        {
-          remove_sound();
-        }
-    }
-
-  // check for a change in mouse settings
-  if (old_mouse != new_mouse)
-    {
-      if (new_mouse > 0.0)   // use OS cursor
-        {
-          set_mouse_sprite(NULL);
-          show_os_cursor(MOUSE_CURSOR_ARROW);
-        }
-      else if (new_mouse == 0.0)     // use Allgero cursor
-        {
-          set_mouse_sprite (my_mouse_image);
-          set_mouse_sprite_focus (0, 0);
-        }
-    }
-  return TRUE;
+	fclose (file);
+	return true;
 }
 
 
-
-
-
-
-/*
-This function catches the close command, usually given by
-the user pressing the close window button. We'll
-try to clean-up.
-
-Note: This function causes the app to hang in Windows.
-Make this compile on non-Windows systems only.
-*/
-void close_button_handler(void)
+static void show_options()
 {
-  // allegro_exit();
-  // exit(0);
-  my_global->close_button_pressed = true;
-}
+	// save old settings
+	bool    temp_sound = env.sound_enabled;
+	int32_t temp_itech = env.itemtechLevel;
+	int32_t temp_wtech = env.weapontechLevel;
 
-int main (int argc, char **argv)
-{
-  int signal;
-  int status;
-  string tmp;
-  ENVIRONMENT *env = NULL;
-  GLOBALDATA *global = NULL;
-  // ifstream configFile;
-  char fullPath[2048];
-  bool load_config_file = true;
-  bool bLoadingSuccess = false;
-  cmdTokens nextToken = ARGV_NOTHING_EXPECTED;
-  double temp_mouse, temp_sound;       // I wish I was not using these
-  int menu_action;
-  int playerCount, player_index;
-  #ifdef NETWORK
-  #ifdef THREADS
-  SEND_RECEIVE_TYPE *send_receive = NULL;
-  #endif
-  int client_socket = -1;
-  #endif
-  bool allow_network = true, allow_thread = false;
-  double music_place_holder;
-  double full_screen = FULL_SCREEN_EITHER;
-  init_cclock_lock();
-
-  quit_right_now = false;
-  global = new GLOBALDATA ();
-  if (!global)
-    {
-      perror ( "Allocating global");
-      exit (1);
-    }
-  my_global = global;
-  print_text_initmsg();
-
-  // try to find data dir
-  if (! global->Find_Data_Dir() )
-     printf("Could not find data dir.\n");
-
-  if (argc >= 2)  		/* Parse command-line switches */
-    {
-      for (int argument = 1; argument < argc; argument++)
-        {
-          tmp = argv[argument];
-
-          if (nextToken == ARGV_GFX_DEPTH)
-            {
-              global->colourDepth = strtol (tmp.c_str(), NULL, 10);
-              if (global->colourDepth != 16 &&
-                  global->colourDepth != 32)
-                {
-                  cout << "Invalid graphics depth, only 16 or 32 are valid\n";
-                  print_text_help();
-                  return 0;
-                }
-              nextToken = ARGV_NOTHING_EXPECTED;
-            }
-          else if (nextToken == ARGV_SCREEN_WIDTH)
-            {
-              global->screenWidth = strtol (tmp.c_str(), NULL, 10);
-              if (global->screenWidth < 512)
-                {
-                  cout << "Width too small (minimum 512)\n";
-                  return 0;
-                }
-              global->width_override = global->screenWidth;
-              global->halfWidth = global->screenWidth / 2;
-              nextToken = ARGV_NOTHING_EXPECTED;
-            }
-          else if (nextToken == ARGV_SCREEN_HEIGHT)
-            {
-              global->screenHeight = strtol (tmp.c_str(), NULL, 10);
-              if (global->screenHeight < 320)
-                {
-                  cout << "Height too small (minimum 320)\n";
-                  return 0;
-                }
-              global->height_override = global->screenHeight;
-              global->halfHeight = global->screenHeight / 2;
-              nextToken = ARGV_NOTHING_EXPECTED;
-            }
-          else if (nextToken == ARGV_DATA_DIR)
-            {
-              // Would use strndup, but the win compiler
-              //   doesn't know of it.
-              if (strlen (tmp.c_str()) > 2048)
-                {
-                  cout << "Datadir path too long:\n"
-                       << "\"" << tmp
-                       << "\"\n\n"
-                       << "Maximum length 2048 characters\n";
-                  return 0;
-                }
-              global->dataDir = strdup (tmp.c_str());
-              nextToken = ARGV_NOTHING_EXPECTED;
-            }
-          else if (nextToken == ARGV_CONFIG_DIR)
-            {
-              if (strlen (tmp.c_str()) > 2048)
-                {
-                  cout << "Configdir path too long:\n"
-                       << "\"" << tmp
-                       << "\"\n\n"
-                       << "Maximum length 2048 characters\n";
-                  return 0;
-                }
-              global->configDir = strdup ( tmp.c_str() );
-              nextToken = ARGV_NOTHING_EXPECTED;
-            }
-          if ( (tmp == SWITCH_HELP) || (tmp == "--help") )
-            {
-              print_text_help();
-              return 0;
-            }
-          else if (tmp == SWITCH_FULL_SCREEN)
-            {
-              screen_mode = GFX_AUTODETECT_FULLSCREEN;
-              full_screen = FULL_SCREEN_TRUE;
-            }
-          else if (tmp == SWITCH_WINDOWED)
-            {
-              screen_mode = GFX_AUTODETECT_WINDOWED;
-              full_screen = FULL_SCREEN_FALSE;
-            }
-          else if (tmp == "-d" || tmp == "--depth")
-            {
-              nextToken = ARGV_GFX_DEPTH;
-            }
-          else if (tmp == "-w" || tmp == "--width")
-            {
-              nextToken = ARGV_SCREEN_WIDTH;
-            }
-          else if (tmp == "-t" || tmp == "--tall")
-            {
-              nextToken = ARGV_SCREEN_HEIGHT;
-            }
-          else if (tmp == "--datadir")
-            {
-              nextToken = ARGV_DATA_DIR;
-            }
-          else if (tmp == "-c")
-            {
-              nextToken = ARGV_CONFIG_DIR;
-            }
-          else if (tmp == "--noconfig")
-            {
-              nextToken = ARGV_NOTHING_EXPECTED;
-              load_config_file = false;
-            }
-          else if (tmp == "--nosound")
-            {
-              nextToken = ARGV_NOTHING_EXPECTED;
-              global->sound = 0.0;
-            }
-          else if (tmp == "--noname")
-            {
-              nextToken = ARGV_NOTHING_EXPECTED;
-              global->name_above_tank = FALSE;
-            }
-          else if (tmp == "--nonetwork")
-          {
-             allow_network = false;
-             nextToken = ARGV_NOTHING_EXPECTED;
-          }
-          else if (tmp == "--nobackground")
-          {
-             global->draw_background = FALSE;
-             nextToken = ARGV_NOTHING_EXPECTED;
-          }
-          else if (tmp == "--nothread")
-          {
-             allow_thread = false;
-             nextToken = ARGV_NOTHING_EXPECTED;
-          }
-          else if (tmp == "--thread")
-          {
-             allow_thread = true;
-             nextToken = ARGV_NOTHING_EXPECTED;
-          }
+	optionsMenu();
 
-        }
-      if (nextToken != ARGV_NOTHING_EXPECTED)
-        {
-          cout << "Expecting an argument to follow " << tmp << endl;
-          return 0;
-        }
-    }
+	if (!Save_Game_Settings(fullPath))
+		cerr << "atanks.cpp:" << __LINE__
+		     << " Failed to save game settings from " << __FUNCTION__ << endl;
 
-  if (! global->configDir)
-    {
-      global->configDir = global->Get_Config_Path();
-
-      // copy the file over, if we did not yet
-      if (!Copy_Config_File(global))
-        {
-          // If it did not work, look whether the directory already exists:
-          DIR * pDestDir;
-          pDestDir = opendir(global->configDir);
-          if (!pDestDir)
-            printf( "An error has occured trying to set up Atomic Tank folders.\n");
-          else
-            {
-              closedir(pDestDir);
-              pDestDir = NULL;
-            }
-        }
-    }       // end of no config file on the command line
-
-
-
-  memset(fullPath, '\0', sizeof(fullPath));
-  FILE *old_config_file = NULL;
-  snprintf(fullPath, sizeof(fullPath) - 1, "%s/atanks-config.txt", global->configDir);
-     if (load_config_file)
-        old_config_file = fopen(fullPath, "r");
-     else
-        old_config_file = NULL;
-     if (old_config_file)
-     {
-         global->loadFromFile_Text(old_config_file);
-         // over-ride full screen setting with command line
-         if ( (full_screen == FULL_SCREEN_TRUE) || (full_screen == FULL_SCREEN_FALSE) )
-            global->full_screen = full_screen;
-         env = init_game_settings(global);
-         if (global->os_mouse)
-             show_os_cursor(MOUSE_CURSOR_ARROW); 
-         global->Load_Text_Files();
-         env->loadFromFile_Text(old_config_file);
-         loadPlayers_Text(global, env, old_config_file); 
-         fclose(old_config_file);
-         bLoadingSuccess = true;
-     }
-     
-
-  if (!bLoadingSuccess)         // no config file found or failed to load
-    {
-      global->numPermanentPlayers = 0;
-      if ( (full_screen == FULL_SCREEN_TRUE) || (full_screen == FULL_SCREEN_FALSE) )
-          global->full_screen = full_screen;
-      env = init_game_settings (global);  
-      global->Load_Text_Files();
-      if (global->os_mouse) show_os_cursor(MOUSE_CURSOR_ARROW);
-      char *defaultNames[] =
-      {
-        "Caesar",
-        "Alex",
-        "Hatshepsut",
-        "Patton",
-        "Napoleon",
-        "Attila",
-        "Catherine",
-        "Hannibal",
-        "Stalin",
-        "Mao"
-      };
-      PLAYER *tempPlayer;
-      tempPlayer = global->createNewPlayer (env);
-      tempPlayer->setName ( global->ingame->complete_text[52] );
-      options (global, env, (MENUDESC*)tempPlayer->menudesc);
-      for (int count = 0; count < 10; count++)
-        {
-          tempPlayer = global->createNewPlayer (env);
-          tempPlayer->type = rand () % (LAST_PLAYER_TYPE - 1) + 1;
-          tempPlayer->setName (defaultNames[count]);
-          tempPlayer->generatePreferences();
-        }
-    }
-
-  status = Load_Weapons_Text(global);
-  if (! status)
-  {
-      printf( "An error occured trying to read weapons file.\n");
-      exit(1);
-  }
-
-  snprintf(fullPath, sizeof(fullPath) - 1, "%s/atanks-config.txt", global->configDir); 
-  global->temp_screenWidth = global->screenWidth;
-  global->temp_screenHeight = global->screenHeight;
-  global->halfWidth = global->screenWidth / 2;
-  global->halfHeight = global->screenHeight / 2;
-  global->menuBeginY = (global->screenHeight - 400) / 2;
-  if (global->menuBeginY < 0) global->menuBeginY = 0;
-  global->menuEndY = global->screenHeight - global->menuBeginY;
-
-  title (global);
-  env->bitmap_filenames = Find_Bitmaps(global, & (env->number_of_bitmaps) );
-  env->my_sky_gradients = (const gradient **) sky_gradients;
-  env->my_land_gradients = (const gradient **) land_gradients;
-  Create_Music_Folder(global);
-#ifdef THREADS
-  if (allow_thread)
-  {
-  pthread_t sky_thread, terrain_thread;
-  pthread_create( &sky_thread, NULL, Generate_Sky_In_Background, (void *) env);
-  pthread_create( &terrain_thread, NULL, Generate_Land_In_Background, (void *) env);
-  }    // end of allowing threads
-#endif
-
-  // new networking area
-  #ifdef THREADS
-  #ifdef NETWORK
-  pthread_t network_thread;
-  if (global->check_for_updates)
-  {
-         global->update_string = Check_For_Update("projects.sourceforge.net",
-                 "version.txt", "atanks.sourceforge.net", VERSION);
-  }
-
-  if ( (global->enable_network) && (allow_network) )
-  {
-      send_receive = (SEND_RECEIVE_TYPE *) calloc(1, sizeof(SEND_RECEIVE_TYPE));
-      if (! send_receive)
-         printf("Could not create networking data.\n");
-  }
-  else
-      send_receive = NULL;
-  if (send_receive)
-  {
-     send_receive->listening_port = (int) global->listen_port;
-     send_receive->global = global;
-     // quit option already cleared by calloc call
-     pthread_create( &network_thread, NULL, Send_And_Receive, (void *) send_receive);
-  }
-  #endif
-  #endif
-   
-  do
-    {
-      //show the main menu
-      global->wr_lock_command();
-      global->command = GLOBAL_COMMAND_MENU;
-      global->unlock_command();
-      signal = menu (global, env);
-      if (global->client_message)
-      {
-         free(global->client_message);
-         global->client_message = NULL;
-      }
-
-      // did the user signal to quit the game
-      global->wr_lock_command();
-      if (signal == SIG_QUIT_GAME) global->command = GLOBAL_COMMAND_QUIT;
-      global->unlock_command();
-
-      //determine which menu item is selected
-      switch (global->get_command())
-        {
-        case GLOBAL_COMMAND_HELP:
-          scrollTextList (global, env, global->instructions);
-          break;
-        case GLOBAL_COMMAND_OPTIONS:
-          // save old settings
-          temp_mouse = global->os_mouse;
-          temp_sound = global->sound;
-
-          options(global, env, NULL);
-          if (! Save_Game_Settings_Text(global, env, fullPath))
-          {
-              perror ( "atanks.cpp: Failed to save game settings from atanks::main()!");
-          }
-
-          // check for changes to settings
-          Change_Settings(temp_mouse, temp_sound, global->os_mouse, global->sound, global->misc[0]);
-          global->Change_Font();
-          global->Update_Player_Menu();
-          break;
-        case GLOBAL_COMMAND_PLAYERS:
-          //loop until done editing players where return value is not ESC
-          do
-          {
-             status = editPlayers(global, env);
-          }
-          while ( (status != KEY_ESC << 8) && (status != KEY_ENTER << 8) );
-          break;
-        case GLOBAL_COMMAND_CREDITS:
-          credits(global, env);
-          break;
-        case GLOBAL_COMMAND_QUIT:
-          break;
-        case GLOBAL_COMMAND_NETWORK:
-          #ifdef NETWORK
-          client_socket = Setup_Client_Socket(global->server_name, global->server_port);
-          if (client_socket >= 0)
-          {
-             int keep_playing = TRUE;
-             printf("Ready to play networked\n");
-             while (keep_playing)
-             {
-                 keep_playing = Game_Client(global, env, client_socket);
-             }
-             Clean_Up_Client_Socket(client_socket);
-          }
-          else
-            printf("Unable to connect to server %s, port %s.\n", global->server_name, global->server_port);
-          #else
-            printf("This version of Atanks is not compiled to handle network games.\n");
-          #endif
-          break;
-        case GLOBAL_COMMAND_DEMO:
-           global->demo_mode = true;
-           global->load_game = false;
-           music_place_holder = global->play_music;
-           global->play_music = 0.0;
-
-           // set up a bunch of players (non-human, less than 10)
-           playerCount = 0;
-           global->numPlayers = 0;
-           for (player_index = 0; player_index < global->numPermanentPlayers; player_index++)
-           {
-               if ( (global->allPlayers[player_index]->type > HUMAN_PLAYER) && (playerCount < MAXPLAYERS) )
-               {
-                  global->addPlayer (global->allPlayers[player_index]);
-                  playerCount++;
-               }
-           }
-
-           player_index = (int) global->skipComputerPlay;
-           global->skipComputerPlay = SKIP_NONE;
-           global->currentround = 1;      // might add more later
-           while ( (global->currentround > 0) && (! global->close_button_pressed) )
-           {
-                game(global, env);
-                if ((global->get_command() == GLOBAL_COMMAND_QUIT) || (global->get_command() == GLOBAL_COMMAND_MENU))
-                      break;
-                global->currentround--;
-                // skipping winner screen for now
-           }
-           endgame_cleanup(global, env);
-           global->demo_mode = false;
-           global->skipComputerPlay = player_index;
-           global->play_music = music_place_holder;
-           break;
-
-        default: //must have commanded to play game
-          menu_action = selectPlayers(global, env);
-          if (menu_action == ESC_MENU)
-            break;
-
-          //selected players, start new game
-          else
-            {
-              // make sure the game has a name
-              if (! global->game_name[0])
-                strcpy(global->game_name,global->ingame->complete_text[53] );
-
-              newgame (global, env);
-
-              // play the game for the selected number of rounds
-              // for (global->currentround = (int)global->rounds; global->currentround > 0; global->currentround--)
-              if (! global->load_game) global->currentround = (int) global->rounds;
-              while ( (global->currentround > 0) && (! global->close_button_pressed) )
-                {
-                  game (global, env);      // play a round
-                  if (global->background_music)
-                  {
-                       stop_sample(global->background_music);
-                       destroy_sample(global->background_music);
-                       global->background_music = NULL;
-                  }
-                      
-                  // if user selected to quit or return to main menu during game play
-                  if ((global->get_command() == GLOBAL_COMMAND_QUIT) || (global->get_command() == GLOBAL_COMMAND_MENU))                 
-                  {
-                    #ifdef NETWORK
-                    global->Send_To_Clients("CLOSE");
-                    #endif
-                    break;
-                  }
-
-                  global->currentround--;
-                  #ifdef NETWORK
-                  if (global->currentround != 0)    // end of the round
-                     global->Send_To_Clients("ROUNDEND");
-                  #endif
-                }
-
-              // only show winner if finished all rounds
-              if ( (global->currentround == 0) && (! global->close_button_pressed) )
-              {
-                  char buffer[256], *my_player;
-                 
-                  // strcpy(buffer, "GAMEEND"); 
-                  // global->Send_To_Clients(buffer);
-                  my_player = do_winner (global, env);
-                  if (my_player)
-                  {
-                     snprintf(buffer, 256, "GAMEEND The game went to %s.", my_player);
-                     free(my_player);
-                  }
-                  else
-                     strcpy(buffer, "GAMEEND");
-                  #ifdef NETWORK
-                  global->Send_To_Clients(buffer);
-                  #endif
-                  do_quote(global, env);
-              }
-
-              endgame_cleanup (global, env);
-            }    // end of start new game
-
-          break;
-
-        }      // end of menu switch
-
-    }
-  while ( (global->get_command() != GLOBAL_COMMAND_QUIT) );
-
-  // print out if there is an update
-  if ( (global->update_string) && (global->update_string[0]) )
-  {
-      cout << global->update_string << endl;
-      free(global->update_string);
-      global->update_string = NULL;
-  }
-
-  #ifdef THREADS
-  #ifdef NETWORK
-  if (send_receive)
-  {
-     send_receive->shut_down = TRUE;
-     // sleep(1);
-     LINUX_REST;
-     // we should probably wait and do a join here, but if the network thread does not
-     // finish after a full second, then something has gone wrong anyway and we should move on....
-     free(send_receive);
-  }
-  #endif
-  #endif
-
-  if (! Save_Game_Settings_Text(global, env, fullPath))
-    {
-      // This is a very critical issue, but as we are ending here, we just report it
-      perror ( "atanks.cpp: Failed to save game settings from atanks::main()!");
-      allegro_exit ();
-      cout << "See http://atanks.sourceforge.net for the latest news and downloads." << endl;
-
-      return(1);
-    }
-  else
-    {
-      Save_Game_Settings_Text(global, env, fullPath);
-      allegro_exit ();
-      cout << "See http://atanks.sourceforge.net for the latest news and downloads." << endl;
-
-      return(0);
-    }
+	// check for changes to settings
+	Change_Settings(temp_sound, temp_itech, temp_wtech);
 }
 
 
-END_OF_MAIN ()
-
-
-
-
-/*
-Adding function here to avoid patch incompatibilties.
-This function should launch all items from all of
-the living tanks.
-*/
-void doLaunch(GLOBALDATA *global, ENVIRONMENT *env)
+static void title()
 {
-  // If we're in simultaneous mode, launch all selections
-  int i;
-  int savestage = env->stage;
-  TANK *tank;
-
-  if (global->turntype != TURN_SIMUL)
-    return;
-
-  for (i = 0; i < global->maxNumTanks; i++)
-    {
-       tank = env->order[i];
-       if (tank)
-        {
-          if (! tank->player->skip_me)
-            tank->activateCurrentSelection();
-          else
-            tank->player->skip_me = false;
-          tank->player->time_left_to_fire = global->max_fire_time;
-        }
-    }
-
-  env->stage = savestage;
+	SHOW_MOUSE(nullptr)
+	blit (env.title[0], screen, 0, 0,
+	      env.halfWidth  - (env.title[0]->w / 2),
+	      env.halfHeight - (env.title[0]->h / 2),
+	      env.title[0]->w, env.title[0]->h);
+	clear_keybuf ();
 }
 
 
-// load a colour from a file
-bool popColo(int &aData, ifstream &ifsFile)
+int32_t main (int32_t argc, char** argv)
 {
-  bool bResult = false;
-  if (ifsFile.is_open())
-  {
-      int iColor = 0;
-      ifsFile >> iColor; // reads the next found number
-      if (ifsFile.good())
-      {
-          // Now transform to color:
-          aData = makecol((iColor & 0x00ff0000) >> 16,
-                          (iColor & 0x0000ff00) >>  8,
-                           iColor & 0x000000ff       );
-          bResult = true;
-       }
-  }
-  return bResult;
-}
+	print_text_initmsg();
 
+	// Parse arguments and exit early if needed
+	int32_t result = parse_args(argc, argv);
 
-int *Sort_Scores(GLOBALDATA *global)
-{
-  static int order[MAXPLAYERS];
-  int counter;
-  bool made_change = true;
-  int temp;
-
-  for (counter = 0; counter < global->numPlayers; counter++)
-    order[counter] = counter;
-
-  // bubble sort
-  while (made_change)
-    {
-      made_change = false;
-      counter = 0;
-      // check for swap
-      while (counter < (global->numPlayers - 1) )
-        {
-          if ( global->players[ order[counter] ]->score < global->players[ order[counter + 1] ]->score )
-            {
-              temp = order[counter];
-              order[counter] = order[counter + 1];
-              order[counter + 1] = temp;
-              made_change = true;
-            }
-
-          counter++;
-        }    // end of check for swaps
-    }         // end of bubble sort
-
-  return order;
-}
+	if (EXIT_FAILURE == result)
+		return EXIT_FAILURE;
+	if (HELP_REQUESTED == result)
+		return EXIT_SUCCESS;
+
+	// try to find data dir
+	if (! env.find_data_dir() ) {
+		cerr << "ERROR: Could not find data dir." << endl;
+		return EXIT_FAILURE;
+	}
 
+	// try to find config dir
+	env.find_config_dir();
 
+	// load or create a configuration
+	if (!loadConfig())
+		createConfig();
 
+	// Load game files
+	if (!env.loadGameFiles())
+		return EXIT_FAILURE; // message already out
 
-// Client version of the game
-// Really, this loop should do some basic things.
-// 1. Find out what the landscape should look like from the server.
-// 2. Place tanks on the battle field
-// 3. Create missiles, beam weapons and such when the server asks us to
-// 4. Get input from the player and forward it to the server.
-// 5. Clean up at the end of the round.
-//
-// Function return TRUE if everything went well or FALSE
-// if an error occured.
+	// new networking area
 #ifdef NETWORK
+	SEND_RECEIVE_TYPE* send_receive   = nullptr;
+	std::thread*       network_thread = nullptr;
+
+	// Create the update checker thread:
+	update_data updateData("projects.sourceforge.net", "version.txt",
+	                       "atanks.sourceforge.net", VERSION);
+
+	std::thread updateThread(std::ref(updateData));
+	if (env.check_for_updates)
+		global.update_string = updateData.update_string;
+
+	// Initialize network if allowed and wanted
+	if ( env.network_enabled && allow_network ) {
+		send_receive = (SEND_RECEIVE_TYPE*)calloc(1, sizeof(SEND_RECEIVE_TYPE));
+		if (!send_receive)
+			cerr << "ERROR: Could not create networking data." << endl;
+	}
+
+	// If a SEND_RECEIVE_TYPE instance was created, start the networking thread
+	if (send_receive) {
+		send_receive->listening_port = env.network_port;
+
+		// quit option already cleared by calloc call
+		network_thread = new std::thread(Send_And_Receive, send_receive);
+	}
+#endif // NETWORK
+
+	/* ===============================
+	 * === The real main main loop ===
+	 * ===============================
+	 */
+	do {
+
+		//show the main menu
+		global.set_command(GLOBAL_COMMAND_MENU);
+		int32_t signal = menu ();
+
+		// Ensure a clean client message
+		if (global.client_message) {
+			free(const_cast<char*>(global.client_message));
+			global.client_message = nullptr;
+		}
+
+		// did the user signal to quit the game?
+		if ( (signal == SIG_QUIT_GAME)
+		  || global.isCloseBtnPressed() )
+			global.set_command(GLOBAL_COMMAND_QUIT);
+
+		// determine which menu item is selected
+		switch (global.get_command()) {
+			case GLOBAL_COMMAND_HELP:
+				scrollTextList (env.instructions);
+				break;
+			case GLOBAL_COMMAND_OPTIONS:
+				show_options();
+			break;
+			case GLOBAL_COMMAND_PLAYERS:
+				editPlayers();
+				break;
+			case GLOBAL_COMMAND_CREDITS:
+				credits();
+				break;
+			case GLOBAL_COMMAND_QUIT:
+				// Already handled by while condition below
+				break;
+			case GLOBAL_COMMAND_NETWORK:
+				play_networked();
+				break;
+			case GLOBAL_COMMAND_DEMO:
+				play_demo();
+				break;
+			default:
+				//must have commanded to play game
+				play_local();
+			break;
+		} // end of menu switch
+	} while ( (global.get_command() != GLOBAL_COMMAND_QUIT) );
+
+	// print out if there is an update
+	if ( (global.update_string) && (global.update_string[0]) ) {
+		cout << global.update_string << endl;
+		global.update_string = nullptr;
+	}
+
+	// Clean up network stuff
+#ifdef NETWORK
+	if (send_receive) {
+		send_receive->shut_down = TRUE;
+		LINUX_REST;
+		network_thread->join();
+		delete network_thread;
+		free(send_receive);
+	}
+	updateThread.join();
+#endif // NETWORK
 
-int Game_Client(GLOBALDATA *global, ENVIRONMENT *env, int socket_number)
-{
-    int surface_x = 1, tank_position = 1, team_number = 1, name_number = 1;
-    int weapon_number = 1, item_number = 1, tank_health = 1;
-    int end_of_round = FALSE, keep_playing = FALSE;
-    int game_stage = CLIENT_VERSION;
-    char buffer[BUFFER_SIZE];
-    int incoming;
-    int my_key;
-    int time_clock = 0;
-    int screen_update = FALSE;
-    int count;      // generic counter
-    int stuff_going_down = FALSE;    // explosions, missiles etc on the screen
-    VIRTUAL_OBJECT *my_object;
-    BEAM *beam;
-    EXPLOSION *explosion;
-    MISSILE *missile;
-    TELEPORT *teleport;
-    FLOATTEXT *floattext;
-    int my_class, object_count;
-    bool fired = false;
-    
-
-    global->dMaxVelocity = (double)MAX_POWER * (100.0 / (double)global->frames_per_second) / 100.0;
-    clear_to_color (env->terrain, PINK);    // get terrain ready
-    clear_to_color(env->db, BLACK);
-
-    // clean up old text
-    for (count = 0; (floattext = (FLOATTEXT*)env->getNextOfClass (FLOATTEXT_CLASS, &count)) && floattext; count++)
-    {
-         floattext->newRound();
-         delete floattext;
+	if (! Save_Game_Settings(fullPath)) {
+		// This is a very critical issue, but as we are ending here, we just report it
+		cerr << "atanks.cpp: Failed to save game settings from atanks::main()!" << endl;
+		result = EXIT_FAILURE;
     }
 
-    Create_Sky(env, global);     // so we have a background
-    strcpy(buffer, "VERSION");
-    write(socket_number, buffer, strlen(buffer));
-
-    while (! end_of_round)
-    {
-        // check for waiting input from the server
-        incoming = Check_For_Incoming_Data(socket_number);
-        if (incoming)
-        {
-           int bytes_read;
-
-           memset(buffer, '\0', BUFFER_SIZE);
-           bytes_read = read(socket_number, buffer, BUFFER_SIZE);  
-           if (bytes_read > 0)
-           {
-                // do something with this input
-                if (! strncmp(buffer, "CLOSE", 5) )
-                {
-                   end_of_round = TRUE;
-                   keep_playing = FALSE;
-                   printf("Got close message.\n");
-                   global->client_message = global->ingame->complete_text[81];
-                }
-                else if (! strncmp(buffer, "NOROOM", 6) )
-                {
-                   end_of_round = TRUE;
-                   keep_playing = FALSE;
-                   printf("The server is full or the game has not started. Please try again later.\n");
-                   global->client_message = global->ingame->complete_text[80];
-                }
-                else if (! strncmp(buffer, "GAMEEND", 7) )
-                {
-                    end_of_round = TRUE;
-                    keep_playing = FALSE;
-                    printf("The game is over.\n");
-                    if ( strlen(buffer) > 7)
-                        global->client_message = strdup(& (buffer[8])) ;
-                    else
-                        global->client_message = strdup(global->ingame->complete_text[82]);
-                }
-                else if (! strncmp(buffer, "ROUNDEND", 8) )
-                {
-                   end_of_round = TRUE;
-                   keep_playing = TRUE;
-                   printf("Round is over.\n");
-                }
-                   
-                else         // not a special command, parse it
-                {
-                  if ( Parse_Client_Data(global, env, buffer) )
-                  {
-                      if (game_stage < CLIENT_PLAYING)
-                         game_stage++;
-                
-                      // Request more information
-                      if (game_stage < CLIENT_PLAYING)
-                      {
-                         switch (game_stage)
-                         {
-                            case CLIENT_SCREEN: strcpy(buffer, "SCREEN"); break;
-                            case CLIENT_WIND: strcpy(buffer, "WIND"); break;
-                            case CLIENT_NUMPLAYERS: strcpy(buffer, "NUMPLAYERS"); break;
-                            case CLIENT_TANK_POSITION: strcpy(buffer, "TANKPOSITION 0"); break;
-                            case CLIENT_SURFACE: strcpy(buffer, "SURFACE 0"); break;
-                            case CLIENT_WHOAMI: strcpy(buffer, "WHOAMI");
-                                                screen_update = TRUE; break;
-                            case CLIENT_WEAPONS: strcpy(buffer, "WEAPON 0"); break;
-                            case CLIENT_ITEMS:   strcpy(buffer, "ITEM 0"); break;
-                            case CLIENT_ROUNDS: strcpy(buffer, "ROUNDS"); break;
-                            case CLIENT_TEAMS: strcpy(buffer, "TEAMS 0"); 
-                                               global->updateMenu = TRUE; break;
-                            case CLIENT_WALL_TYPE: strcpy(buffer, "WALLTYPE"); break;
-                            case CLIENT_BOXED: strcpy(buffer, "BOXED"); break;
-                            case CLIENT_NAME: strcpy(buffer, "PLAYERNAME 0"); break;
-                            case CLIENT_TANK_HEALTH: strcpy(buffer, "HEALTH 0"); break;
-                            default: buffer[0] = '\0';
-                         }
-                         write(socket_number, buffer, strlen(buffer));
-                       }   // end of getting more info
-                   }    // our game stage went up
-                   else  // we got data, but our game stage did not go up
-                   {
-                       if (fired)
-                       {
-                           if ( (global->client_player) && (global->client_player->tank) )
-                           {
-                               fired = false;
-                               if (global->client_player->tank->cw < WEAPONS)
-                                   sprintf(buffer, "WEAPON %d", global->client_player->tank->cw);
-                               else
-                                   sprintf(buffer, "ITEM %d", global->client_player->tank->cw - WEAPONS);
-                               write(socket_number, buffer, strlen(buffer));
-                           }
-                       }
-                       else if (game_stage == CLIENT_SURFACE)
-                       {
-                           sprintf(buffer, "SURFACE %d", surface_x);
-                           write(socket_number, buffer, strlen(buffer));
-                           surface_x++;
-                       }
-                       else if (game_stage == CLIENT_ITEMS)
-                       {
-                            sprintf(buffer, "ITEM %d", item_number);
-                            write(socket_number, buffer, strlen(buffer));
-                            item_number++;
-                       }
-                       else if (game_stage == CLIENT_TANK_POSITION)
-                       {
-                            sprintf(buffer, "TANKPOSITION %d", tank_position);
-                            write(socket_number, buffer, strlen(buffer));
-                            tank_position++;
-                            if (tank_position >= global->numPlayers)
-                              tank_position = 0;
-                       }
-                       else if (game_stage == CLIENT_TANK_HEALTH)
-                       {
-                            sprintf(buffer, "HEALTH %d", tank_health);
-                            write(socket_number, buffer, strlen(buffer));
-                            tank_health++;
-                            if (tank_health >= global->numPlayers)
-                                tank_health = 0;
-                       }
-                       else if (game_stage == CLIENT_TEAMS)
-                       {
-                            sprintf(buffer, "TEAMS %d", team_number);
-                            write(socket_number, buffer, strlen(buffer));
-                            team_number++;
-                       }
-                       else if (game_stage == CLIENT_NAME)
-                       {
-                            sprintf(buffer, "PLAYERNAME %d", name_number);
-                            write(socket_number, buffer, strlen(buffer));
-                            name_number++;
-                       }
-                       else if (game_stage == CLIENT_WEAPONS)
-                       {
-                            sprintf(buffer, "WEAPON %d", weapon_number);
-                            write(socket_number, buffer, strlen(buffer));
-                            weapon_number++;
-                       }
-                       else if (game_stage == CLIENT_PLAYING)
-                       {
-                           time_clock++;
-                           if (time_clock > 1)   // check positions every few inputs
-                           {
-                               time_clock = 0;
-                               if (surface_x < global->screenWidth)
-                               {
-                                   game_stage = CLIENT_SURFACE;
-                                   sprintf(buffer, "SURFACE %d", surface_x);
-                                   write(socket_number, buffer, strlen(buffer));
-                                   surface_x++;
-                               }
-                               else
-                               {
-                                 game_stage = CLIENT_TANK_POSITION;
-                                 tank_position = 1;
-                                 strcpy(buffer, "TANKPOSITION 0");
-                                 write(socket_number, buffer, strlen(buffer));
-                               }    // game stage stuff
-                           }
-                       }        // end of playing commands
-                   }
-
-                }      // end of we got something besides the close command
-
-           }
-           else    // connection was broken
-           {
-              close(socket_number);
-              printf("Server closed connection.\n");
-              end_of_round = TRUE;
-           }
-        }
+	env.destroy();
+	global.destroy();
 
-        object_count = 0;
-        while (object_count < MAX_OBJECTS)
-        {
-            my_object = env->objects[object_count];
-            if (my_object)
-            {
-            my_class = my_object->getClass();
-            if (my_class == BEAM_CLASS)
-            {
-                beam = (BEAM *) my_object;
-                beam->applyPhysics();
-                if (beam->destroy)
-                {
-                    beam->requireUpdate();
-                    beam->update();
-                    delete beam;
-                }
-                stuff_going_down = TRUE;
-            }
-            else if (my_class == MISSILE_CLASS)
-            {
-                missile = (MISSILE *) my_object;
-                missile->hitSomething = 0;
-                missile->applyPhysics();
-                missile->triggerTest();
-                if (missile->destroy)
-                {
-                    missile->requireUpdate();
-                    missile->update();
-                    delete missile;
-                }
-                stuff_going_down = TRUE;
-            }
-            else if (my_class == EXPLOSION_CLASS)
-            {
-                explosion = (EXPLOSION *) my_object;
-                explosion->explode ();
-                explosion->applyPhysics ();
-                if (explosion->destroy)
-                {
-                  explosion->requireUpdate ();
-                  explosion->update ();
-                  delete explosion;
-                }
-                stuff_going_down = TRUE;
-            }
-            else if (my_class == TELEPORT_CLASS)
-            {
-                teleport = (TELEPORT *) my_object;
-                teleport->applyPhysics ();
-                if (teleport->destroy)
-                {
-                  teleport->requireUpdate ();
-                  teleport->update ();
-                  delete teleport;
-                  time_clock = 2;
-                }
-                stuff_going_down = TRUE;
-            }
-            else if (my_class == FLOATTEXT_CLASS)
-            {
-                floattext = (FLOATTEXT *) my_object;
-                floattext->applyPhysics ();
-                if (floattext->destroy)
-                {
-                  floattext->requireUpdate();
-                  floattext->update();
-                  delete floattext;
-                }
-            }
-            else if (my_class == DECOR_CLASS)
-            {
-                 DECOR *decor = (DECOR *) my_object;
-                 decor->applyPhysics ();
-                 if (decor->destroy)
-                 {
-                    decor->requireUpdate ();
-                    decor->update ();
-                    delete decor;
-                 }
-            }
-            }      // end of got valid object
-            object_count++;
-        }
+	allegro_exit();
 
-        slideLand(global, env);
+	cout << "See http://atanks.sourceforge.net for the latest news and downloads." << endl;
 
-        // update everything on the screen
-        if (global->updateMenu)
-        {
-          set_clip_rect (env->db, 0, 0, (global->screenWidth-1), MENUHEIGHT - 1);
-          drawTopBar (global, env, env->db);
-        }
-
-        set_clip_rect (env->db, 0, MENUHEIGHT, (global->screenWidth-1), (global->screenHeight-1));
-        if (screen_update)
-        {
-          blit (env->sky, env->db, global->window.x, global->window.y - MENUHEIGHT, global->window.x, global->window.y, (global->window.w - global->window.x) + 1, (global->window.h - global->window.y) + 1);
-          masked_blit (env->terrain, env->db, global->window.x, global->window.y, global->window.x, global->window.y, (global->window.w - global->window.x) + 1, (global->window.h - global->window.y) + 2);
-          drawTopBar(global, env, env->db);
-          int iLeft = 0;
-          int iRight = global->screenWidth - 1;
-          int iTop = MENUHEIGHT;
-          int iBottom = global->screenHeight - 1;
-          set_clip_rect (env->db, 0, 0, iRight, iBottom);
-          vline(env->db, iLeft, iTop, iBottom, env->wallColour);    // Left edge
-          vline(env->db, iRight, iTop, iBottom, env->wallColour);   // right edge
-          hline(env->db, iLeft, iBottom, iRight, env->wallColour);// bottom edge
-          if (global->bIsBoxed)
-              hline(env->db, iLeft, iTop, iRight, env->wallColour);// top edge
-
-          env->make_update(0, 0, global->screenWidth, global->screenHeight);
-        }
-        else
-        {
-          env->replaceCanvas ();
-        }
-
-        for (count = 0; count < global->numPlayers; count++)
-        {
-             if ( (global->players[count]) && (global->players[count]->tank) )
-             {
-                global->players[count]->tank->drawTank(env->db, 0);
-                global->players[count]->tank->update();
-             }
-        }
-        // env->do_updates();
-        screen_update = TRUE;
-
-        object_count = 0;
-        while (object_count < MAX_OBJECTS)
-        {
-            my_object = env->objects[object_count];
-            if (my_object)
-            {
-               my_class = my_object->getClass();
-
-               if (my_class == BEAM_CLASS)
-               {
-                  beam = (BEAM *) my_object;
-                  beam->draw (env->db);
-                  beam->update ();
-                }
-                else if (my_class == MISSILE_CLASS)
-                {
-                    missile = (MISSILE *) my_object;
-                    missile->draw(env->db);
-                    missile->update();
-                }
-                else if (my_class == EXPLOSION_CLASS)
-                {
-                   explosion = (EXPLOSION *) my_object;
-                   explosion->draw (env->db);
-                   explosion->update ();
-                }
-                else if (my_class == TELEPORT_CLASS)
-                {
-                     teleport = (TELEPORT *) my_object;
-                     if (teleport->object)
-                        teleport->draw (env->db);
-                     teleport->update ();
-                }
-                else if (my_class == DECOR_CLASS)
-                {
-                     DECOR *decor = (DECOR *) my_object;
-                     decor->draw(env->db);
-                     decor->update();
-                }
-                else if (my_class == FLOATTEXT_CLASS)
-                {
-                     floattext = (FLOATTEXT *) my_object;
-                     floattext->draw (env->db);
-                     floattext->requireUpdate ();
-                     floattext->update ();
-               }
-            }       // end of valid object
-            object_count++;
-        }     // end of while updating objects
-        env->do_updates();
-
-        
-        // check for input from the user
-        if ( keypressed() )
-        {
-           my_key = readkey();
-           my_key = my_key >> 8;
-           if (my_key == KEY_SPACE)
-           {
-              Client_Fire(global->client_player, socket_number);
-              fired = true;
-           }
-           else if (my_key == KEY_ESC)
-           {
-              end_of_round = TRUE;
-              close(socket_number);
-           }
-           else if (my_key == KEY_UP)
-           {
-              Client_Power(global->client_player, CLIENT_UP);
-           }
-           else if (my_key == KEY_DOWN)
-           {
-              Client_Power(global->client_player, CLIENT_DOWN);
-           }
-           else if (my_key == KEY_LEFT)
-           {
-              Client_Angle(global->client_player, CLIENT_LEFT);
-           }
-           else if (my_key == KEY_RIGHT)
-           {
-              Client_Angle(global->client_player, CLIENT_RIGHT);
-           }
-           else if ( (my_key == KEY_Z) || (my_key == KEY_BACKSPACE) )
-           {
-               Client_Cycle_Weapon(global->client_player, CYCLE_BACK);
-           }
-           else if ( (my_key == KEY_C) || (my_key == KEY_TAB) )
-           {
-               Client_Cycle_Weapon(global->client_player, CYCLE_FORWARD);
-               global->updateMenu = TRUE;
-           }
-
-           screen_update = FALSE;
-           global->updateMenu = TRUE;
-        }
-
-        // pause for a moment
-        // if (game_stage < CLIENT_PLAYING)
-        if (stuff_going_down)
-        {
-            LINUX_SLEEP;
-            stuff_going_down = FALSE;
-        }
-    }
-
-    // we should clean up here
-    for (count = 0; count < global->numPlayers; count++)
-    {
-       if (global->players[count]->tank)
-       {
-           delete global->players[count]->tank;
-           global->players[count]->tank = NULL;
-       }
-    }
- 
-    return keep_playing;
+	return result;
 }
-
-#endif
-
+END_OF_MAIN()
diff --git a/src/atanks.rc b/src/atanks.rc
old mode 100644
new mode 100755
index 52a074d..96a03bf
--- a/src/atanks.rc
+++ b/src/atanks.rc
@@ -1,37 +1,96 @@
-/* THIS FILE WILL BE OVERWRITTEN BY DEV-C++ */
-/* DO NOT EDIT! */
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+/////////////////////////////////////////////////////////////////////////////
+// Deutsch (Deutschland) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
+#pragma code_page(1252)
 
-#include <windows.h> // include for version info constants
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
 
-A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "../atanks.ico"
-ALLEGRO_ICON ICON "../atanks.ico"
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+A                       ICON                    "../atanks.ico"
+ALLEGRO_ICON            ICON                    "../atanks.ico"
 
+/////////////////////////////////////////////////////////////////////////////
 //
-// TO CHANGE VERSION INFORMATION, EDIT PROJECT OPTIONS...
+// Version
 //
+
 1 VERSIONINFO
-FILEVERSION 3,2,0,24
-PRODUCTVERSION 3,2,0,24
-FILETYPE VFT_APP
-{
-  BLOCK "StringFileInfo"
-     {
-         BLOCK "100904E4"
-         {
-             VALUE "CompanyName", ""
-             VALUE "FileVersion", ""
-             VALUE "FileDescription", "A fun tank game, which plays like Scorched Earth."
-             VALUE "InternalName", ""
-             VALUE "LegalCopyright", "See credits.txt and COPYING.txt"
-             VALUE "LegalTrademarks", ""
-             VALUE "OriginalFilename", "atanks.exe"
-             VALUE "ProductName", "Atomic Tanks"
-             VALUE "ProductVersion", ""
-         }
-     }
-  BLOCK "VarFileInfo"
-     {
-         VALUE "Translation", 0x1009, 1252
-     }
-}
+ FILEVERSION 6,1,2,0
+ PRODUCTVERSION 6,1,2,0
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904e4"
+        BEGIN
+            VALUE "FileDescription", "Atomic Tanks"
+            VALUE "FileVersion", "6.1.2.0"
+            VALUE "LegalCopyright", "See credits.txt and COPYING.txt"
+            VALUE "OriginalFilename", "atanks.exe"
+            VALUE "ProductName", "Atomic Tanks"
+            VALUE "ProductVersion", "6.1.2.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1252
+    END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // Deutsch (Deutschland) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
 
diff --git a/src/beam.cpp b/src/beam.cpp
index c7e768e..4f98566 100644
--- a/src/beam.cpp
+++ b/src/beam.cpp
@@ -24,263 +24,490 @@
 #include "decor.h"
 #include "tank.h"
 #include "beam.h"
+#include "explosion.h"
+#include "sound.h"
 
+#include <cassert>
 
-BEAM::~BEAM ()
+
+// Static helper for the drawing methods
+static int32_t beamRadius = 1;
+static int32_t beamSeed   = 0;
+
+// Helper methods for the drawing methods
+static void lazerPoint    (BITMAP *dest, int32_t x1, int32_t y1, int32_t color);
+static void lightningPoint(BITMAP *dest, int32_t x1, int32_t y1, int32_t age);
+
+
+/// @brief BEAM constructor
+BEAM::BEAM (PLAYER* player_, double x_, double y_, int32_t fireAngle,
+            int32_t weaponType, eBeamType beam_type) :
+	PHYSICAL_OBJECT(BT_WEAPON == beam_type),
+	beamType       (beam_type),
+	tgtRightX      (env.screenWidth)
 {
-  requireUpdate();
-  update();
-  _env->make_bgupdate (_current.x, _current.y, _current.w, _current.h);
-  _env->make_bgupdate (_old.x, _old.y, _old.w, _old.h);
-  _env->removeObject (this);
-  weap    = NULL;
-  _global = NULL;
-  _env    = NULL;
-   delete [] points;
+	this->player   = player_;
+	this->weapType = weaponType;
+
+	assert( ( ((weapType >= SML_LIGHTNING) && (weapType <= LRG_LIGHTNING))
+		    ||((weapType >= SML_LAZER)     && (weapType <= LRG_LAZER)) )
+		 && "ERROR: BEAM ctor called with something else than Lightning or Laser!");
+
+#ifdef NETWORK
+	char buffer[256];
+	sprintf(buffer, "BEAM %d %d %d %d", (int) x_, (int) y_, fireAngle, weaponType);
+	env.sendToClients(buffer);
+#endif // NETWORK
+
+	x     = x_;
+	y     = y_;
+	angle = fireAngle % 360;
+	xv    = env.slope[angle][0];
+	yv    = env.slope[angle][1];
+
+
+	if (weapType < WEAPONS) weap = &(weapon[weapType]);
+	else                    weap = &(naturals[weapType - WEAPONS]);
+	radius = weap->radius;
+
+	/* All beams should have the same age, no matter what the FPS settings
+	 * are.
+	 * Based on the default of 60 frames per second, the following frame
+	 * lengths are wanted:
+	 * Small lightning :  5 frames ( 1/12 second)
+	 * Medium lightning: 10 frames ( 1/ 6 second)
+	 * Large lightning : 15 frames ( 3/12 second)
+	 * Lightning increase : 5 frames per size = 1/12 FPS
+	 * Small laser     : 10 frames ( 1/6 second)
+	 * Medium lightning: 20 frames ( 1/3 second)
+	 * Large lightning : 30 frames ( 1/2 second)
+	 * Laser increase     : 10 frames per size = 1/6 FPS
+	*/
+	int32_t age_per_size = env.frames_per_second / 12; // Doubled for laser
+	int32_t base_age     = 5; // Doubled for laser
+	int32_t weap_size    = 0; // aka "small"
+
+	if ( (weapType >= SML_LIGHTNING) && (weapType <= LRG_LIGHTNING) ) {
+		numPoints = 4 + (rand () % 9); // 4 - 12
+		weap_size = weapType - SML_LIGHTNING;
+	} else if ( (weapType >= SML_LAZER) && (weapType <= LRG_LAZER) ) {
+		base_age     *= 2;
+		age_per_size *= 2;
+		numPoints     = 2;
+		weap_size     = weapType - SML_LAZER;
+		if (BT_SDI != beamType)
+			// The SDI constructor produces its own color
+			color = makecol(255 - ((weapType - SML_LAZER) * 64),
+			                    128,
+			                     64 + ((weapType - SML_LAZER) * 64));
+		if ( !global.skippingComputerPlay
+		  && ( (BT_WEAPON == beamType) || (BT_SDI == beamType) ) )
+			play_fire_sound(weapType, x, 128 + (radius * 10), 1500 - (radius * 50));
+	}
+
+	maxAge = base_age + (age_per_size * weap_size);
+	damage = static_cast<double>(weap->damage) / static_cast<double>(maxAge);
+
+	// Set an offset seed
+	seed = rand() % std::max(env.screenWidth, env.screenHeight);
+
+	createBeamPath();
+
+	// Now that the points are clear, a lightning bolt can emit its thunder:
+	if ( !global.skippingComputerPlay
+	  && (BT_NATURAL == beamType) )
+		play_natural_sound(weapType, (points[0].x + points[numPoints - 1].x) / 2,
+		                   175 + (radius * 10), 1000);
+
+	// Add to the chain unless it is a mind shot:
+	if (BT_MIND_SHOT != beamType)
+		global.addObject(this);
 }
 
-void tracePath (int *targetX, int *targetY, GLOBALDATA *global, ENVIRONMENT *env, int minRange, double xpos, double ypos, int angle)
+
+/// @brief special constructor for SDI lasers
+BEAM::BEAM(PLAYER* player_, double x_, double y_, double tx, double ty,
+           int32_t weaponType, bool is_burnt_out) :
+	BEAM(player_, x_, y_, GET_ANGLE(std::abs(ty - y_), tx - x_) + 90,
+		weaponType, BT_SDI)
 {
-  double dx = global->slope[angle][0];
-  double dy = global->slope[angle][1];
-  double tx = xpos, ty = ypos;
-  int hitSomething = 0;
-  int range = 0;
-
-  while ((!hitSomething) && tx > -10 &&
-         tx < global->screenWidth + 10 && ty > -10 &&
-         ty < global->screenHeight &&
-         (getpixel (env->terrain, (int)tx, (int)ty) == PINK))
-    {
-      TANK *ltank;
-      for (int objCount = 0; (ltank = (TANK*)env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-        {
-          if (range > minRange && tx > ltank->x - TANKWIDTH && tx < ltank->x + TANKWIDTH && ty > ltank->y && ty < ltank->y + TANKHEIGHT && ltank->l > 0)
-            {
-              hitSomething = 1;
-              ltank->requireUpdate ();
-            }
-        }
-      tx += dx;
-      ty += dy;
-      range++;
-    }
-  *targetX = (int)tx;
-  *targetY = (int)ty;
+	if (player)
+		++player->sdiShots;
+
+	// SDI lasers are redder than normal, even more if burnt_out
+	color = makecol(is_burnt_out ? 255 : 240 - ((weapType - SML_LAZER) * 16),
+	                    is_burnt_out ?  32 :  64,
+	                    is_burnt_out ? (weapType - SML_LAZER) * 32 : 128);
+
+	// Limit the laser to the missiles coordinates
+	points[numPoints - 1].x = tx;
+	points[numPoints - 1].y = ty;
 }
 
-BEAM::BEAM (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, int fireAngle,
-            int weaponType):PHYSICAL_OBJECT(),weap(NULL),points(NULL)
+
+
+/// @brief BEAM destructor
+BEAM::~BEAM ()
 {
-  player = NULL;
-  drag = 0.00;
-  mass = 0;
-  clock = 500;
-  age = 0;
-  setEnvironment (env);
-  _align = LEFT;
-  _global = global;
-  #ifdef NETWORK
-  char buffer[256];
-  sprintf(buffer, "BEAM %d %d %d %d", (int) xpos, (int) ypos, fireAngle, weaponType);
-  global->Send_To_Clients(buffer);
-  #endif
-  x = xpos;
-  y = ypos;
-  angle = fireAngle % 360;
-  type = weaponType;
-  if (type < WEAPONS)
-    weap = &(weapon[type]);
-  else
-    weap = &(naturals[type - WEAPONS]);
-  radius = weap->radius;
-
-
-  if (type >= SML_LIGHTNING && type <= LRG_LIGHTNING)
-    {
-      damage = (double)weap->damage / (10 * (type - SML_LIGHTNING + 1));
-      maxAge = 10 * (type - SML_LIGHTNING + 1);
-      numPoints = 4 + rand () % 10;
-      color = WHITE;
-      play_sample ((SAMPLE *) _global->sounds[LIGHTNING_SOUND], env->scaleVolume(128 + (radius * 10)), 128, 500 + (radius * 50), 0);
-    }
-  else
-    {
-      numPoints = 2;
-      if (type >= SML_LAZER && type <= LRG_LAZER)
-        damage = (double)weap->damage / (10 * (type - SML_LAZER + 1));
-      maxAge = 10 * (type - SML_LAZER + 1);
-      color = makecol (255 - (type - SML_LAZER) * 64, 128, 64 + (type - SML_LAZER) * 64);
-    }
-  tracePath (&targetX, &targetY, global, env, radius + 2, x, y, angle);
-  xv = targetX - x;
-  yv = targetY - y;
-  points = new int[numPoints * 2];
-  if (!points)
-    {
-      perror ( "beam.cc: Failed allocating memory for points in BEAM::BEAM");
-      // exit (1);
-    }
-  setLightningPath ();
+	requireUpdate();
+	update();
+	weap   = nullptr;
+	if (points)
+		delete [] points;
+	points = nullptr;
+
+	if (BT_MIND_SHOT != beamType) {
+		global.make_bgupdate (dim_cur.x, dim_cur.y, dim_cur.w, dim_cur.h);
+		global.make_bgupdate (dim_old.x, dim_old.y, dim_old.w, dim_old.h);
+
+		// Let the land slide where the beam burned through:
+		global.addLandSlide(tgtLeftX, tgtRightX, false);
+
+		// Apply damage to all hit tanks:
+		TANK* lt = nullptr;
+		global.getHeadOfClass(CLASS_TANK, &lt);
+		while (lt) {
+			lt->applyDamage();
+			lt->getNext(&lt);
+		}
+
+		// Take out of the chain:
+		global.removeObject(this);
+
+		// The player is allowed to fire one more SDI laser again:
+		if ((BT_SDI == beamType) && player)
+			--player->sdiShots;
+	}
 }
 
-void BEAM::setLightningPath ()
+
+void BEAM::applyPhysics ()
 {
-  int point = 0;
-  double x1 = x, y1 = y;
-
-  do
-    {
-      double offX, offY;
-      if (point > 0 && point < numPoints - 1)
-        {
-          offX = (perlin2DPoint (1.0, 20, 12746 + x1, y1, 0.3, 6) + 1) * (radius * 10);
-          offY = (perlin2DPoint (1.0, 20, x1, 1273 + y1, 0.3, 6) + 1) * (radius * 10);
-        }
-      else
-        {
-          offX = 0;
-          offY = 0;
-        }
-      points[point * 2] = (int)(x1 + offX);
-      points[point * 2 + 1] = (int)(y1 + offY);
-
-      x1 += xv / (numPoints - 1);
-      y1 += yv / (numPoints - 1);
-      point++;
-    }
-  while (point < numPoints);
+	if (++age > maxAge)
+		destroy = true;
+
+	if (BT_SDI != beamType) {
+		createBeamPath();
+	}
+
+	if (BT_MIND_SHOT != beamType) {
+		if ( !global.skippingComputerPlay
+		  && !(rand() % (env.frames_per_second / 5)) ) {
+			try {
+				new DECOR ( points[numPoints-1].x,
+							points[numPoints-1].y,
+							(rand () % 7) - 3,
+							1 - (rand () % 6), radius, DECOR_SMOKE, 0);
+			} catch (std::exception) {
+				perror ( "beam.cpp: Failed to allocate memory for decor in applyPhysics");
+			}
+		}
+
+		try {
+			new EXPLOSION(player, points[numPoints-1].x, points[numPoints-1].y,
+			              points[numPoints-1].x - points[0].x,
+			              points[numPoints-1].y - points[0].y,
+			              weapType, damage, isWeaponFire);
+		} catch (std::exception) {
+			perror ( "beam.cpp: Failed to allocate memory for explosion in applyPhysics");
+		}
+	}
 }
 
-void BEAM::initialise ()
+
+void BEAM::draw()
 {
-  PHYSICAL_OBJECT::initialise ();
-  drag = 0.00;
-  mass = 0;
-  clock = 500;
-  age = 0;
+	// never draw mind shots!
+	if (BT_MIND_SHOT == beamType)
+		return;
+
+	int32_t oldDrawingMode = global.current_drawing_mode;
+
+	drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
+	global.current_drawing_mode = DRAW_MODE_TRANS;
+	set_trans_blender (0, 0, 0, 50);
+
+	beamRadius = radius;
+	beamSeed   = seed;
+
+	for (int32_t i = 1; i < numPoints; ++i) {
+		int32_t left   = std::min(points[i - 1].x, points[i].x);
+		int32_t top    = std::min(points[i - 1].y, points[i].y);
+		int32_t right  = std::max(points[i - 1].x, points[i].x);
+		int32_t bottom = std::max(points[i - 1].y, points[i].y);
+
+		if ( (weapType >= SML_LIGHTNING) && (weapType <= LRG_LIGHTNING) )
+			do_line (global.canvas,
+			         points[i - 1].x, points[i - 1].y,
+			         points[i    ].x, points[i    ].y,
+			         age, lightningPoint);
+		else if ( (weapType >= SML_LAZER) && (weapType <= LRG_LAZER) )
+			do_line (global.canvas,
+			         points[i - 1].x, points[i - 1].y,
+			         points[i    ].x, points[i    ].y,
+			         color, lazerPoint);
+
+		addUpdateArea (left - radius, top - radius,
+		               right  - left + (2 * radius),
+		               bottom - top  + (2 * radius) );
+	}
+
+	drawing_mode(oldDrawingMode, NULL, 0, 0);
+	global.current_drawing_mode = oldDrawingMode;
+
+	requireUpdate ();
 }
 
-int BEAM::applyPhysics ()
+
+// === Private method implementations ===
+// ======================================
+
+/// @brief Create the basic points array with path tracing
+void BEAM::createBeamPath()
 {
-  TANK *ltank;
-  DECOR *decor;
-
-  age++;
-  circlefill (_env->terrain, targetX, targetY, radius, PINK);
-  for (int col = targetX - radius - 1; col < targetX + radius + 1; col++)
-    setSlideColumnDimensions (_global, _env, col, true);
-
-  if (age > maxAge)
-    destroy = TRUE;
-
-  for (int objCount = 0; (ltank = (TANK*)_env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-    {
-      if (targetX > ltank->x - TANKWIDTH - radius && targetX < ltank->x + TANKWIDTH + radius && targetY > ltank->y - radius && targetY < ltank->y + TANKHEIGHT + radius && ltank->l > 0)
-        {
-          // hitSomething = 1;
-          ltank->requireUpdate ();
-
-          // make sure a player is involved
-          if (player)
-            ltank->damage += damage * player->damageMultiplier;
-          else
-            ltank->damage += damage;
-
-          ltank->creditTo = player;
-          if (destroy)
-            ltank->applyDamage ();
-        }
-    }
-  decor = new DECOR (_global, _env, targetX, targetY, rand () % 6 - 3, rand () % 6 - 3, radius, DECOR_SMOKE);
-  if (!decor)
-    {
-      perror ( "beam.cc: Failed allocating memory for decor in applyPhysics");
-      // exit (1);
-    }
-  tracePath (&targetX, &targetY, _global, _env, radius + 2, x, y, angle);
-  setLightningPath ();
-
-  return (hitSomething);
+	if (nullptr == points) {
+		try {
+			points = new POINT_t[numPoints];
+		} catch (std::exception) {
+			perror ( "beam.cpp: Failed to allocate memory for points in BEAM::createBeamPath()");
+		}
+	}
+
+	// First determine the direct target - where does the beam end?
+	double  tx = x, ty = y;
+	hitSomething = false;
+
+	// If this is not the first call, use the already known endpoints
+	if ( ( points[0].x             || points[0].y
+		|| points[numPoints - 1].x || points[numPoints - 1].y)
+	  && !global.isDirtInBox( points[0].x, points[0].y,
+	                               points[numPoints - 1].x,
+	                               points[numPoints - 1].y) ) {
+		tx = points[numPoints - 1].x;
+		ty = points[numPoints - 1].y;
+	} else {
+		// The first point is the starting point, the last will become the target
+		points[0].x = x;
+		points[0].y = y;
+	}
+
+	while ( !hitSomething
+	     && (tx > -radius)
+	     && (tx < (env.screenWidth + radius))
+	     && (ty > -radius)
+	     && (ty < (env.screenHeight + radius)) ) {
+
+		// Assume PINK for off screen pixels
+		int32_t col = PINK;
+
+		if ( (tx > 0)
+		  && (tx < (env.screenWidth - 1))
+		  && (ty > MENUHEIGHT)
+		  && (ty < (env.screenHeight - 1)) )
+			col = getpixel (global.terrain, tx, ty);
+
+		if (PINK == col) {
+			tx += xv;
+			ty += yv;
+		} else
+			hitSomething = true;
+	} // End of tracing pixels
+
+	// tx and ty now result in the first obstacle (or screen border)
+	// on a direct path.
+	points[numPoints - 1].x = tx;
+	points[numPoints - 1].y = ty;
+
+	// If this is a lightning strike, points between the first and last
+	// have to be (re-)generated.
+	makeLightningPath();
+
+	// Generate new lightning offsets checking for collisions:
+	traceBeamPath();
+
+	// If this is a mind_shot, it is immediately destroyed
+	if (BT_MIND_SHOT == beamType)
+		destroy = true;
 }
 
-int beamRadius;
-void lazerPoint (BITMAP *dest, int x1, int y1, int color)
+
+/// @brief get the end of a mind shot laser
+void BEAM::getEndPoint(int32_t& x, int32_t& y)
 {
-  circlefill (dest, x1, y1, beamRadius, color);
+	x = points[numPoints - 1].x;
+	y = points[numPoints - 1].y;
 }
 
-void lightningPoint (BITMAP *dest, int x1, int y1, int age)
+
+/// @brief create the lightning steps between the beginning and the end
+void BEAM::makeLightningPath()
 {
-  double pointRad = (perlin2DPoint (1.0, 2, x1 + age, y1, 0.3, 6) + 1) / 2 * beamRadius + 1;
-  double offX = (perlin2DPoint (1.0, 20, 127846 + x1, y1 + age, 0.3, 6) + 1) * beamRadius;
-  double offY = (perlin2DPoint (1.0, 20, x1 + age, 12973 + y1, 0.3, 6) + 1) * beamRadius;
+	if ( (numPoints > 2)
+	  && (weapType >= SML_LIGHTNING)
+	  && (weapType <= LRG_LIGHTNING) ) {
+	  	int32_t maxP     = numPoints - 1;
+		double  stepping = FABSDISTANCE2(points[0].x,    points[0].y,
+		                                 points[maxP].x, points[maxP].y) / maxP;
+
+		for (int32_t i = 1; i < maxP; ++i) {
+			points[i].x = x
+			            + (xv * (static_cast<double>(i) * stepping))
+			            + (perlin2DPoint (1.0, 10. * radius,
+			                        points[i].x + seed, points[i].y,
+			                        0.3, 6) * radius * 10.);
+			points[i].y = y
+			            + (yv * (static_cast<double>(i) * stepping))
+			            + (perlin2DPoint (1.0, 10. * radius,
+			                        points[i].x, points[i].y + seed,
+			                        0.3, 6) * radius * 10.);
+		}
+	} // End of lightning preparation
+}
 
-  circlefill (dest, x1 + (int)offX, y1 + (int)offY, (int)pointRad, WHITE);
+
+/// @brief this method is used by the satellite to move the beam with itself.
+void BEAM::moveStart(double x_, double y_)
+{
+	x = x_;
+	y = y_;
+	if (points) {
+		points[0].x = x;
+		points[0].y = y;
+	}
 }
 
-void BEAM::draw (BITMAP *dest)
+
+/// @brief walk through the beam points and check whether anything is hit
+void BEAM::traceBeamPath()
 {
-  int x1, y1, x2, y2;
-  setUpdateArea ((int)x - radius, (int)y - radius, (int)x + radius * 2, (int)y + radius * 2);
-
-  drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
-  _env->current_drawing_mode = DRAW_MODE_TRANS;
-  set_trans_blender (0, 0, 0, (int)50);
-  if (type >= SML_LIGHTNING && type <= LRG_LIGHTNING)
-    {
-      beamRadius = radius;
-      for (int point = 1; point < numPoints; point++)
-        {
-
-          x1 = points[(point - 1) * 2];
-          y1 = points[(point - 1) * 2 + 1];
-          x2 = points[point * 2];
-          y2 = points[point * 2 + 1];
-
-          do_line (dest, x1, y1, x2, y2, age, lightningPoint);
-          addUpdateArea (MIN(x1,x2) - radius * 6,
-                         MIN(y1,y2) - radius * 6,
-                         MAX(x1,x2) + radius * 2 * 6,
-                         MAX(y1,y2) + radius * 2 * 6);
-        }
-    }
-  else if (type >= SML_LAZER && type <= LRG_LAZER)
-    {
-      beamRadius = radius;
-      for (int point = 1; point < numPoints; point++)
-        {
-
-          x1 = points[(point - 1) * 2];
-          y1 = points[(point - 1) * 2 + 1];
-          x2 = points[point * 2];
-          y2 = points[point * 2 + 1];
-
-          do_line (dest, x1, y1, x2, y2, color, lazerPoint);
-          //do_line (dest, x1, y1, x2, y2, color, lightningPoint);
-          addUpdateArea (MIN(x1,x2) - radius * 2,
-                         MIN(y1,y2) - radius * 2,
-                         MAX(x1,x2) + radius * 2 * 2,
-                         MAX(y1,y2) + radius * 2 * 2);
-        }
-    }
-  drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
-  _env->current_drawing_mode = DRAW_MODE_SOLID;
-  requireUpdate ();
+	int32_t minRange = radius + 2;
+	bool    canHit   = false;
+
+	hitSomething = false;
+
+	for (int32_t i = 1; !hitSomething && (i < numPoints); ++i) {
+		double  startX   = points[i-1].x;
+		double  startY   = points[i-1].y;
+		double  endX     = points[i].x;
+		double  endY     = points[i].y;
+		bool    chkTanks = (BT_SDI == beamType)
+		                 ? false
+		                 : global.areTanksInBox(startX, startY, endX, endY);
+		bool    chkDirt  = global.isDirtInBox  (startX, startY, endX, endY);
+
+		// Break this if there is nothing possibly in between
+		if (!(chkTanks || chkDirt) )
+			continue;
+
+		int32_t range    = 0;
+		double  distX    = endX - startX;
+		double  distY    = endY - startY;
+		double  absX     = std::abs(distX);
+		double  absY     = std::abs(distY);
+		double  moveX    = distX / (absX > absY ? absX : absY);
+		double  moveY    = distY / (absY > absX ? absY : absX);
+		int32_t toMove   = ROUND(std::max(absX, absY));
+
+		// Now wander along the path:
+		while ( !hitSomething
+			  && (range < toMove)
+		      && (startX > 0)
+		      && (startX < (env.screenWidth - 1))
+		      && (startY > MENUHEIGHT)
+		      && (startY < (env.screenHeight - 1))
+			  && ( !chkDirt
+				|| (PINK == getpixel (global.terrain, startX, startY))) ) {
+
+			// Only check for tanks if the total range is large enough
+			// and if there are tanks in the path
+			if ((range >= minRange) && !canHit)
+				canHit = true;
+			if (canHit && chkTanks) {
+				TANK*  lt        = nullptr;
+				global.getHeadOfClass(CLASS_TANK, &lt);
+				while (lt) {
+					// Tank found, is it hit?
+					if (!lt->destroy
+					  && lt->isInBox(startX - radius, startY - radius,
+									 startX + radius, startY + radius) ) {
+					  	hitSomething = true;
+						lt->requireUpdate();
+
+						// 'Lock' the beam end on the tank:
+						if (startY < (lt->y + radius) )
+							startY = lt->y + radius;
+						if (startY > (lt->y + (2 * radius)) )
+							startY = lt->y + (2 * radius);
+						if (startX < (lt->x - (radius / 2)))
+							startX = lt->x - (radius / 2);
+						if (startX > (lt->x + (radius / 2)))
+							startX = lt->x + (radius / 2);
+
+						// Get the in_rates
+						double in_rate_x, in_rate_y;
+						if ( (BT_MIND_SHOT != beamType)
+						  && lt->isInEllipse(startX, startY, radius, radius,
+						                     in_rate_x, in_rate_y) ) {
+							double in_rate = in_rate_x * in_rate_y;
+							if (in_rate < 0.9)
+								// Beams do not 'splash'.
+								in_rate = 0.9;
+
+							lt->addDamage(player, static_cast<double>(damage)
+									   * in_rate
+									   * (player ? player->damageMultiplier : 1.) );
+						}
+						// That's it
+						lt = nullptr;
+						moveX = 0.;
+						moveY = 0.;
+					} // End of having a tank
+					else
+						lt->getNext(&lt);
+				} // End of looping tanks
+			}
+
+			// Advance startX/Y
+			startX += moveX;
+			startY += moveY;
+			++range;
+		} // End of regular check
+
+		// If dirt was hit, hitSomething must be adapted
+		if ((PINK != getpixel (global.terrain, startX, startY)))
+			hitSomething = true;
+
+		if (hitSomething
+		  && ( (ROUND(startX) != points[numPoints - 1].x)
+		    || (ROUND(startY) != points[numPoints - 1].y) ) ) {
+			// Reset the points to the new circumstances:
+			points[numPoints - 1].x = ROUND(startX);
+			points[numPoints - 1].y = ROUND(startY);
+			makeLightningPath();
+
+			// Note down x position for dirt slide on destruction:
+			if ((startX - radius - 1) < tgtLeftX)  tgtLeftX  = startX - radius - 1;
+			if ((startX + radius + 1) > tgtRightX) tgtRightX = startX + radius + 1;
+		} // End of checking pixels
+	} // End of looping points
 }
 
-/*void BEAM::update ()
+
+// === static helper methods ===
+// =============================
+static void lazerPoint (BITMAP *dest, int32_t x1, int32_t y1, int32_t color)
 {
-	_env->combineUpdates = FALSE;
-	VIRTUAL_OBJECT::update ();
-	_env->combineUpdates = TRUE;
-}*/
+	circlefill (dest, x1, y1, beamRadius, color);
+}
 
-int BEAM::isSubClass (int classNum)
+static void lightningPoint (BITMAP *dest, int32_t x1, int32_t y1, int32_t age)
 {
-  if (classNum == BEAM_CLASS)
-    return (TRUE);
-  else
-    return (FALSE);
-  //return (PHYSICAL_OBJECT::isSubClass (classNum));
+	double pRad = (perlin2DPoint (1.0, 2,  x1 + age, y1 + beamSeed, 0.3, 6) + 1)
+	            / 2 * beamRadius + 1;
+	double offX = (perlin2DPoint (1.0, 10 * pRad, x1 + age + beamSeed, y1 + age, 0.3, 6) + 1)
+	            * pRad / 2.;
+	double offY = (perlin2DPoint (1.0, 10 * pRad, x1 + age, y1 + age + beamSeed, 0.3, 6) + 1)
+	            * pRad / 2.;
+
+	circlefill (dest, x1 + offX, y1 + offY, pRad, WHITE);
 }
diff --git a/src/beam.h b/src/beam.h
index 7bdfaa7..3f50828 100644
--- a/src/beam.h
+++ b/src/beam.h
@@ -21,54 +21,80 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
 #include "main.h"
-#include "virtobj.h"
 #include "physobj.h"
 
-#define LIGHTNING_SOUND 12
 
-enum beamType
+/** @enum eBeamType
+  * @brief Determines what kind of beam is generated
+**/
+enum eBeamType
 {
-  LIGHTNING_BEAM,
-  LAZER_BEAM
+	BT_WEAPON = 0, //!< Normal weapon, nothing special
+	BT_SDI,        //!< Not a weapon but an SDI laser
+	BT_NATURAL,    //!< Fired by natural disaster, like lightning.
+	BT_MIND_SHOT   //!< AI thinking.
 };
 
+
 class BEAM: public PHYSICAL_OBJECT
-  {
-  public:
-    int	radius;
-    int	length;
-    double	damage;
-    int	clock;
-    int	type;
-    WEAPON	*weap;
-    int	*points;	// Allows jagged lines
-    int	numPoints;
-    int	color;
-    int	targetX;
-    int	targetY;
-
-    virtual ~BEAM ();
-    //BEAM (GLOBALDATA *global, ENVIRONMENT *env, double tX, double tY, double tXv, double tYv, int weaponType);
-    BEAM (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, int angle, int weaponType);
-    void	setLightningPath();
-    void	initialise ();
-    void	draw (BITMAP *dest);
-    //void	update ();
-    int	applyPhysics ();
-    virtual int	isSubClass (int classNum);
-    inline virtual int	getClass ()
-    {
-      return (BEAM_CLASS);
-    }
-    inline virtual void setEnvironment(ENVIRONMENT *env)
-    {
-      if (!_env || (_env != env))
-        {
-          _env = env;
-          _index = _env->addObject (this);
-        }
-    }
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit BEAM ( PLAYER* player_, double x_, double y_,
+					int32_t fireAngle, int32_t weaponType,
+					eBeamType beam_type);
+	BEAM          ( PLAYER* player_, double x_, double y_,
+					double tx, double ty, int32_t weaponType,
+					bool is_burnt_out);
+	~BEAM ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void	 applyPhysics();
+    void	 draw        ();
+    void     getEndPoint (int32_t &x, int32_t &y); // For mind shots to fetch
+	void     moveStart   (double x_, double y_);   // For the satellite
+
+	eClasses getClass() { return CLASS_BEAM; }
+
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void createBeamPath();
+	void makeLightningPath();
+	void traceBeamPath ();
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	eBeamType beamType  = BT_WEAPON;
+	int32_t   color     = WHITE;
+	double    damage    = 0.;
+	int32_t   numPoints = 0;
+	POINT_t*  points    = nullptr;
+	int32_t   radius    = 0;
+	int32_t   seed      = 0;
+	int32_t   tgtLeftX  = 0;
+	int32_t   tgtRightX = 0;
+	WEAPON*   weap      = nullptr;
+};
 
 #endif
 
diff --git a/src/button.cpp b/src/button.cpp
index ff845ad..8207016 100644
--- a/src/button.cpp
+++ b/src/button.cpp
@@ -2,66 +2,138 @@
 This file contains funcions for the BUTTON class. These
 are being moved out of the atanks.cc file.
 -- Jesse
+
+Updated to be more variable and self managing
+-- Sven
 */
 
 #include "button.h"
+#include "sound.h"
+
 
-BUTTON::BUTTON(GLOBALDATA *global, ENVIRONMENT *env, int x1, int y1,
-               char *text1, BITMAP *bmp1, BITMAP *hover1, BITMAP *depressed1):_global(NULL),_env(NULL),text(NULL),
-               bmp(NULL),hover(NULL),depressed(NULL),click(NULL)
+/** @brief Create a graphical button with no optional title and default sound
+  *
+  * Note: if @a click_ is nullptr, global_->sounds[8] is used.
+  */
+BUTTON::BUTTON(int32_t left_, int32_t top_,
+               BITMAP* bmp_, BITMAP* hover_, BITMAP* depressed_) :
+	bmp      (bmp_       ? bmp_       : hover_ ? hover_ : depressed_ ? depressed_ : nullptr),
+	depressed(depressed_ ? depressed_ : hover_ ? hover_ : bmp_       ? bmp_       : nullptr),
+	hover    (hover_     ? hover_     : bmp_   ? bmp_   : depressed_ ? depressed_ : nullptr)
 {
-  _global = global;
-  _env = env;
-  location.x = x1;
-  location.y = y1;
-  text = text1;
-  click = (SAMPLE *)_global->sounds[8];
-  bmp = bmp1;
-  hover = hover1;
-  depressed = depressed1;
-  location.w = bmp->w;
-  location.h = bmp->h;
-  xl = location.x + bmp->w;
-  yl = location.y + bmp->h;
+	location.x = left_;
+	location.y = top_;
+	location.w = bmp ? bmp->w  : 0;
+	location.h = bmp ? bmp->h : 0;
+	x1 = location.x;
+	y1 = location.y;
+	x2 = location.w + x1;
+	y2 = location.h + y1;
+	x3 = x1 + (location.w / 2);
+	y3 = y1 + (location.h / 2);
 }
 
 
+/** @brief Create a graphical button
+  *
+  * Note: if @a click_ is nullptr, global_->sounds[8] is used.
+  */
+BUTTON::BUTTON(const char* text_, bool text_only_,
+               int32_t left_, int32_t top_,
+               BITMAP* bmp_, BITMAP* hover_, BITMAP* depressed_) :
+	BUTTON(left_, top_, bmp_, hover_, depressed_)
+{
+	text_only  = text_only_;
+	text       = text_;
+	text_width = text ? text_length(font, text) : 0;
+}
+
 
-void BUTTON::draw (BITMAP *dest)
+/** @brief Create a button with manual drawing
+  */
+BUTTON::BUTTON(const char* text_, bool text_only_,
+               int32_t left_, int32_t top_, int32_t width_, int32_t height_) :
+	BUTTON(text_, text_only_, left_, top_,
+			nullptr, nullptr, nullptr)
 {
-  draw_sprite (dest, (BITMAP *)(isMouseOver()?(isPressed()?depressed:hover):bmp), location.x, location.y);
-  if (text)
-    textout_centre_ex (dest, font, text, location.x+75, location.y + 6, WHITE, -1);
-  _env->make_update (location.x, location.y, xl, yl);
+	location.w = width_;
+	location.h = height_;
+	x2 = location.w + x1;
+	y2 = location.h + y1;
+	x3 = x1 + (location.w / 2);
+	y3 = y1 + (location.h / 2);
 }
 
 
-int BUTTON::isMouseOver ()
+
+
+void BUTTON::draw()
 {
-  if ((mouse_x >= location.x) &&
-      (mouse_y >= location.y) &&
-      (mouse_x <= xl) &&
-      (mouse_y <= yl))
-    {
-      return (1);
-    }
-  else
-    {
-      return (0);
-    }
+	bool mouse_over = isMouseOver();
+	bool pressed    = isPressed();
+
+	if (!text_only) {
+		if (bmp)
+			draw_sprite (global.canvas,
+				mouse_over ? (pressed ? depressed : hover) : bmp,
+				x1, y1);
+		else {
+			int32_t fg = mouse_over ? (pressed ? SILVER : GREY)   : WHITE;
+			int32_t lc = mouse_over ? (pressed ? GREY   : WHITE)  : SILVER;
+			int32_t dc = mouse_over ? (pressed ? WHITE  : SILVER) : GREY;
+
+			rect     (global.canvas, x1    , y1    , x2    , y2    , BLACK);
+			rectfill (global.canvas, x1 + 1, y1 + 1, x2 - 1, y2 - 1, fg);
+			line     (global.canvas, x2 - 2, y2 - 2, x1 + 2, y2 - 2, dc);
+			line     (global.canvas, x2 - 3, y2 - 3, x1 + 3, y2 - 3, dc);
+			line     (global.canvas, x2 - 2, y2 - 2, x2 - 2, y1 + 2, dc);
+			line     (global.canvas, x2 - 3, y2 - 3, x2 - 3, y1 + 3, dc);
+			line     (global.canvas, x1 + 2, y1 + 2, x2 - 2, y1 + 2, lc);
+			line     (global.canvas, x1 + 3, y1 + 3, x2 - 3, y1 + 3, lc);
+			line     (global.canvas, x1 + 2, y1 + 2, x1 + 2, y2 - 2, lc);
+			line     (global.canvas, x1 + 3, y1 + 3, x1 + 3, y2 - 3, lc);
+			rect     (global.canvas, x1 + 4, y1 + 4, x2 - 4, y2 - 4, BLACK);
+		}
+	}
+
+	if (text)
+		textout_centre_ex (global.canvas, font, text, x3, y3 - (text_height(font) / 2),
+							text_only ? BLACK : WHITE, -1);
+
+	global.make_update (x1 - 5, y1 - 5, x2 + 5, y2 + 5);
 }
 
 
-int BUTTON::isPressed()
+void BUTTON::getLocation(int32_t &x,int32_t &y,int32_t &w,int32_t &h)
 {
-  if ((mouse_b == 1) && isMouseOver ())
-    {
-      play_sample (click, _env->scaleVolume(128), 128, 1000, 0);
-      return 1;
-    }
-  else
-    {
-      return 0;
-    }
+	x = location.x;
+	y = location.y;
+	w = location.w;
+	h = location.h;
 }
 
+
+bool BUTTON::isMouseOver ()
+{
+	if ( (mouse_x >= x1)
+	  && (mouse_y >= y1)
+	  && (mouse_x <= x2)
+	  && (mouse_y <= y2) )
+		return true;
+	return false;
+}
+
+
+bool BUTTON::isPressed()
+{
+	if ((mouse_b & 3) && isMouseOver ()) {
+		play_interface_sound(SND_INTE_BUTTON_CLICK);
+		return true;
+	}
+	return false;
+}
+
+void BUTTON::setText(const char* text_)
+{
+	text = text_;
+}
diff --git a/src/button.h b/src/button.h
index 6dfe362..1c04d7b 100644
--- a/src/button.h
+++ b/src/button.h
@@ -1,30 +1,59 @@
 #ifndef BUTTON_HEADER_
 #define BUTTON_HEADER_
 
-#include "globaldata.h"
-#include "environment.h"
+#include "main.h"
 
 class BUTTON
-  {
-    GLOBALDATA *_global;
-    ENVIRONMENT *_env;
-
-  public:
-    BOX location;
-    int xl, yl;
-    char *text;
-    BITMAP *bmp;
-    BITMAP *hover;
-    BITMAP *depressed;
-    SAMPLE *click;
-
-    BUTTON (GLOBALDATA *global, ENVIRONMENT *env, int x1, int y1, char *text1,
-            BITMAP *bmp1, BITMAP *hover1, BITMAP *depressed1);
-
-    int     isPressed (); //if button pressed returns 1
-    int     isMouseOver (); //Cursor is over button
-    void    draw (BITMAP *dest);
-  };
+{
+public:
+
+	/* --------------------
+	 * --- constructors ---
+	 * --------------------
+	 */
+
+	// Minimum ctor without text
+	explicit
+	BUTTON (int32_t left_, int32_t top_,
+	        BITMAP* bmp_, BITMAP* hover_, BITMAP* depressed_);
+
+	// ctor for using a bitmap.
+	BUTTON (const char* text_, bool text_only_,
+	        int32_t left_, int32_t top_,
+	        BITMAP* bmp_, BITMAP* hover_, BITMAP* depressed_);
+
+	// ctor for drawing a manual box.
+	BUTTON (const char* text_, bool text_only_,
+	        int32_t left_, int32_t top_, int32_t width_, int32_t height_);
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void draw ();
+	void getLocation(int32_t &x,int32_t &y,int32_t &w,int32_t &h);
+	bool isMouseOver ();
+	bool isPressed ();
+	void setText(const char* text_);
+
+private:
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	BITMAP*     bmp        = nullptr;
+	BITMAP*     depressed  = nullptr;
+	BITMAP*     hover      = nullptr;
+	BOX         location;               //!< is {0, 0, 0, 0} by default
+	const char* text       = nullptr;
+	bool        text_only  = false;     //!< If set to true, only the title is displayed.
+	int32_t     text_width = 0;         //!< must not be recalculated over and over again...
+	int32_t     x1, y1, x2, y2, x3, y3; //!< Shortcuts, as those stay fixed.
+};
 
 #endif
 
diff --git a/src/client.cpp b/src/client.cpp
index bd2eb68..e1a2e4a 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -1,8 +1,4 @@
-#ifdef NETWORK
-
-#include "menu.h"
 #include "button.h"
-#include "team.h"
 #include "files.h"
 #include "satellite.h"
 #include "update.h"
@@ -13,51 +9,52 @@
 #include "missile.h"
 #include "teleport.h"
 #include "floattext.h"
+#include "player.h"
+#include "tank.h"
 
 #include "sky.h"
-#ifdef THREADS
-#include <pthread.h>
-#endif
-
-
 
+// Note: Don't guard everything. Empty compilation units are invalid.
+#ifdef NETWORK
 
+// From gameloop.cpp:
+void draw_top_bar();
 
 // Here we try to match the buffer with an action. We then attempt to
 // perform the action. Remember, this is a command from the server, so
 // it is either giving us some info or telling us to create something.
-int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
+int Parse_Client_Data(char *buffer)
 {
    char args[CLIENT_ARGS][BUFFER_SIZE];
    char letter;
    int dest_string;
    int line_length = strlen(buffer);
-   int source_index = 0, dest_index = 0;
-  
+   int sourceindex = 0, destindex = 0;
+
    // clear buffers
    for (dest_string = 0; dest_string < CLIENT_ARGS; dest_string++)
        memset(args[dest_string], '\0', BUFFER_SIZE);
 
-   dest_string = 0; 
+   dest_string = 0;
    // copy buffer into cmd and argument variables
-   while ( ( source_index < line_length ) && (dest_string < CLIENT_ARGS) )
-   { 
-       letter = buffer[source_index];
+   while ( ( sourceindex < line_length ) && (dest_string < CLIENT_ARGS) )
+   {
+       letter = buffer[sourceindex];
        if ( letter == ' ' )
        {
            letter = '\0';
-           args[dest_string][dest_index] = letter;
-           dest_index = 0;
+           args[dest_string][destindex] = letter;
+           destindex = 0;
            dest_string++;
        }
        else
        {
-          args[dest_string][dest_index] = letter;
-          dest_index++;
+          args[dest_string][destindex] = letter;
+          destindex++;
        }
-       source_index++;
+       sourceindex++;
    }
-  
+
    // let us see what we have
    if (! strcmp(args[0], "SERVERVERSION") )
    {
@@ -70,10 +67,10 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
    }
    else if (! strcmp(args[0], "CURRENTPOSITION") )
    {
-      if ( (global->client_player) && (global->client_player->tank) )
+      if ( (global.client_player) && (global.client_player->tank) )
       {
-          sscanf(args[1], "%lf", &(global->client_player->tank->x));
-          sscanf(args[2], "%lf", &(global->client_player->tank->y));
+          sscanf(args[1], "%lf", &(global.client_player->tank->x));
+          sscanf(args[2], "%lf", &(global.client_player->tank->y));
       }
    }
    else if (! strcmp(args[0], "BEAM") )
@@ -84,16 +81,16 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
       sscanf(args[2], "%lf", &my_y);
       sscanf(args[3], "%d", &my_angle);
       sscanf(args[4], "%d", &my_type);
-      new BEAM(global, env, my_x, my_y, my_angle, my_type);
+      new BEAM(nullptr, my_x, my_y, my_angle, my_type, BT_WEAPON);
    }
    else if (! strcmp(args[0], "BOXED"))
    {
        int got_box;
        sscanf(args[1], "%d", &got_box);
        if (got_box)
-           global->bIsBoxed = true;
+           env.isBoxed = true;
        else
-           global->bIsBoxed = false;
+           env.isBoxed = false;
        return TRUE;
    }
    else if (! strcmp(args[0], "EXPLOSION") )
@@ -103,53 +100,53 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
         sscanf(args[1], "%lf", &my_x);
         sscanf(args[2], "%lf", &my_y);
         sscanf(args[3], "%d", &my_type);
-        new EXPLOSION(global, env, my_x, my_y, my_type);
+        new EXPLOSION(nullptr, my_x, my_y, 0., 0., my_type, true);
         return FALSE;
    }
    else if (! strcmp(args[0], "ITEM"))
    {
-       int item_index, amount;
-       sscanf(args[1], "%d", &item_index);
+       int itemindex, amount;
+       sscanf(args[1], "%d", &itemindex);
        sscanf(args[2], "%d", &amount);
-       if ( (item_index >= 0) && (item_index < ITEMS) &&
+       if ( (itemindex >= 0) && (itemindex < ITEMS) &&
             (amount >= 0) && (amount <= 99) )
        {
-          global->client_player->ni[item_index] = amount;
+          global.client_player->ni[itemindex] = amount;
        }
-       if (item_index == (ITEMS - 1) )
+       if (itemindex == (ITEMS - 1) )
           return TRUE;
    }
    else if (! strcmp(args[0], "HEALTH") )
    {
-       int tank_index;
+       int tankindex;
        int health, shield, shield_type;
        char some_text[32];
 
-       sscanf(args[1], "%d", &tank_index);
-       if (tank_index >= 0)
+       sscanf(args[1], "%d", &tankindex);
+       if (tankindex >= 0)
        {
           sscanf(args[2], "%d", &health );
           sscanf(args[3], "%d", &shield );
           sscanf(args[4], "%d", &shield_type);
-          global->players[tank_index]->tank->l = health;
-          global->players[tank_index]->tank->sh = shield;
-          global->players[tank_index]->tank->sht = shield_type;
+          env.players[tankindex]->tank->l = health;
+          env.players[tankindex]->tank->sh = shield;
+          env.players[tankindex]->tank->sht = shield_type;
           // set the text over the tank
           sprintf(some_text, "%d", health);
-          global->players[tank_index]->tank->healthText->set_text(some_text);
-          global->players[tank_index]->tank->healthText->set_color(global->players[tank_index]->color);
+          env.players[tankindex]->tank->healthText.set_text(some_text);
+          env.players[tankindex]->tank->healthText.set_color(env.players[tankindex]->color);
           sprintf(some_text, "%d", shield);
-          global->players[tank_index]->tank->shieldText->set_text(some_text);
-          global->players[tank_index]->tank->healthText->set_color(global->players[tank_index]->color);
+          env.players[tankindex]->tank->shieldText.set_text(some_text);
+          env.players[tankindex]->tank->healthText.set_color(env.players[tankindex]->color);
        }
-       if (tank_index == (global->numPlayers - 1) )
+       if (tankindex == (env.numGamePlayers - 1) )
           return TRUE;
        else
           return FALSE;
    }
    else if (! strcmp(args[0], "WIND") )
    {
-       sscanf(args[1], "%lf", & (env->wind) );
+       sscanf(args[1], "%lf", & (global.wind) );
        return TRUE;
    }
    else if (! strcmp(args[0], "MISSILE") )
@@ -162,7 +159,8 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
        sscanf(args[3], "%lf", &delta_x);
        sscanf(args[4], "%lf", &delta_y);
        sscanf(args[5], "%d", &my_type);
-       missile = new MISSILE(global, env, my_x, my_y, delta_x, delta_y, my_type);
+       missile = new MISSILE(nullptr, my_x, my_y, delta_x, delta_y, my_type,
+							MT_WEAPON, 1);
        if (! missile)
          printf("Attempted to create missile failed in client code.\n");
        return FALSE;
@@ -170,15 +168,14 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
    else if (! strcmp(args[0], "NUMPLAYERS") )
    {
       int counter;
-      sscanf(args[1], "%d", & (global->numPlayers) );
+      sscanf(args[1], "%d", & (env.numGamePlayers) );
       // create the players in question
-      for (counter = 0; counter < global->numPlayers; counter++)
+      for (counter = 0; counter < env.numGamePlayers; counter++)
       {
-          global->players[counter] = new PLAYER(global, env);
-          global->players[counter]->tank = new TANK(global, env);
-          global->players[counter]->tank->player = global->players[counter];
-          global->players[counter]->tank->initialise();
-          global->players[counter]->tank->nameText->set_text("");
+          env.players[counter] = new PLAYER();
+          env.players[counter]->tank = new TANK();
+          env.players[counter]->tank->player = env.players[counter];
+          env.players[counter]->tank->nameText.set_text(nullptr);
       }
       return TRUE;
    }
@@ -192,29 +189,29 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
    {
        int number;
        sscanf(args[1], "%d", &number);
-       if ( (number < global->numPlayers) && (number >= 0) )
-           global->players[number]->setName(args[2]);
-       if (number == (global->numPlayers - 1) )
-          return TRUE;    
+       if ( (number < env.numGamePlayers) && (number >= 0) )
+           env.players[number]->setName(args[2]);
+       if (number == (env.numGamePlayers - 1) )
+          return TRUE;
    }
    else if (! strcmp(args[0], "REMOVETANK") )
    {
        int index;
        sscanf(args[1], "%d", &index);
-       if ( (index >= 0) && (index < global->numPlayers) )
+       if ( (index >= 0) && (index < env.numGamePlayers) )
        {
            // make sure this tank exists before we get rid of it
-           if ( global->players[index]->tank )
+           if ( env.players[index]->tank )
            {
-               delete global->players[index]->tank;
-               global->players[index]->tank = NULL;
+               delete env.players[index]->tank;
+               env.players[index]->tank = NULL;
            }
        }
    }
    else if (! strcmp(args[0], "ROUNDS") )
    {
-       sscanf(args[1], "%lf", &global->rounds);
-       sscanf(args[2], "%d", &global->currentround);
+       sscanf(args[1], "%u", &env.rounds);
+       sscanf(args[2], "%u", &global.currentround);
        return TRUE;
    }
    else if (! strcmp(args[0], "SURFACE") )
@@ -227,13 +224,13 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
 
        sscanf(args[1], "%d", &x);
        sscanf(args[2], "%d", &y);
-       env->surface[x] = y;
-       my_height = global->screenHeight - y;
+       global.surface[x].store(y);
+       my_height = env.screenHeight - y;
        my_height = my_height / 50;      // ratio of change
        // fill in terrain...
-       for (index = y; index < global->screenHeight; index++)
+       for (index = y; index < env.screenHeight; index++)
        {
-           putpixel(env->terrain, x, index, makecol(0, green, 0));
+           putpixel(global.terrain, x, index, makecol(0, green, 0));
            colour_change++;
            if (colour_change >= my_height)
            {
@@ -241,7 +238,7 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
               green--;
            }
        }
-       if (x >= (global->screenWidth - 1) )
+       if (x >= (env.screenWidth - 1) )
          return TRUE;
    }
    else if (! strcmp(args[0], "SCREEN") )
@@ -250,14 +247,14 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
 
        sscanf(args[1], "%d", &width);
        sscanf(args[2], "%d", &height);
-       if ( (width == global->screenWidth) &&
-            (height == global->screenHeight) )
+       if ( (width == env.screenWidth) &&
+            (height == env.screenHeight) )
            printf("Host's screen resolution matches ours.\n");
        else
        {
            printf("Host's screen resolution is %d by %d.\n", width, height);
            printf("Ours is %d by %d. This is going to cause problems!\n",
-                  global->screenWidth, global->screenHeight);
+                  env.screenWidth, env.screenHeight);
         }
        return TRUE;
    }
@@ -267,7 +264,7 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
        PLAYER *my_player;
 
        sscanf(args[1], "%d", &player_number);
-       my_player = global->players[player_number];
+       my_player = env.players[player_number];
        if ( (my_player) && (my_player->tank) )
        {
            sscanf(args[2], "%d", &x);
@@ -275,30 +272,32 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
            my_player->tank->x = x;
            my_player->tank->y = y;
        }
-       if (player_number == (global->numPlayers - 1))
+       if (player_number == (env.numGamePlayers - 1))
          return TRUE;
    }
    else if (! strcmp(args[0], "TEAM") )
    {
-       int player_number, colour;
-       double the_team;
+       int32_t player_number = 0;
+       int32_t  colour = BLACK;
+       int the_team;
        sscanf(args[1], "%d", &player_number);
-       sscanf(args[2], "%lf", & the_team);
-       if ( (the_team < global->numPlayers) && (the_team >= 0) )
+       sscanf(args[2], "%d", & the_team);
+       if ( (the_team < env.numGamePlayers) && (the_team >= 0) )
        {
-            global->players[player_number]->team = the_team;
+            env.players[player_number]->team =
+				static_cast<eTeamTypes>(the_team);
             if (the_team == TEAM_JEDI)
                 colour = makecol(0, 255, 0);
             else if (the_team == TEAM_SITH)
                 colour = makecol(255, 0, 255);
             else if (the_team == TEAM_NEUTRAL)
                 colour = makecol(0, 0, 255);
-            if (global->players[player_number] == global->client_player)
+            if (env.players[player_number] == global.client_player)
                 colour = makecol(255, 0, 0);
-            global->players[player_number]->color = colour;
+            env.players[player_number]->color = colour;
        }
-       if (player_number == (global->numPlayers - 1) )
-          return TRUE; 
+       if (player_number == (env.numGamePlayers - 1) )
+          return TRUE;
    }
    else if (! strcmp(args[0], "TELEPORT") )
    {
@@ -308,55 +307,55 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
        sscanf(args[1], "%d", &player_num);
        sscanf(args[2], "%d", &new_x);
        sscanf(args[3], "%d", &new_y);
-       if ( (player_num >= 0) && (player_num < global->numPlayers) &&
-            (global->players[player_num]->tank) )
+       if ( (player_num >= 0) && (player_num < env.numGamePlayers) &&
+            (env.players[player_num]->tank) )
        {
-          new TELEPORT(global, env, global->players[player_num]->tank,
-                       new_x, new_y, TANKHEIGHT * 4 + GUNLENGTH, 120);
+			TANK* lt = env.players[player_num]->tank;
+          new TELEPORT(lt, new_x, new_y, lt->getDiameter(), 120, ITEM_TELEPORT);
        }
-           
+
    }
    else if (! strcmp(args[0], "WALLTYPE"))
    {
-        sscanf(args[1], "%d", &(env->current_wallType));
-        switch (env->current_wallType)
+        sscanf(args[1], "%d", &(env.current_wallType));
+        switch (env.current_wallType)
         {
            case WALL_RUBBER:
-           env->wallColour = makecol(0, 255, 0); // GREEN;
+           env.wallColour = makecol(0, 255, 0); // GREEN;
            break;
            case WALL_STEEL:
-           env->wallColour = makecol(255, 0, 0); // RED;
+           env.wallColour = makecol(255, 0, 0); // RED;
            break;
            case WALL_SPRING:
-           env->wallColour = makecol(0, 0, 255); //BLUE;
+           env.wallColour = makecol(0, 0, 255); //BLUE;
            break;
            case WALL_WRAP:
-           env->wallColour = makecol(255, 255, 0); // YELLOW;
+           env.wallColour = makecol(255, 255, 0); // YELLOW;
            break;
         }
        return TRUE;
    }
    else if (! strcmp(args[0], "WEAPON") )
    {
-       int weapon_index, amount;
-       sscanf(args[1], "%d", &weapon_index);
+       int weaponindex, amount;
+       sscanf(args[1], "%d", &weaponindex);
        sscanf(args[2], "%d", &amount);
-       if ( (weapon_index >= 0) && (weapon_index < WEAPONS) &&
+       if ( (weaponindex >= 0) && (weaponindex < WEAPONS) &&
             (amount >= 0) && (amount <= 99) )
        {
-            global->client_player->nm[weapon_index] = amount;
+            global.client_player->nm[weaponindex] = amount;
        }
-       if (weapon_index == (WEAPONS - 1))
+       if (weaponindex == (WEAPONS - 1))
           return TRUE;
    }
    else if (! strcmp(args[0], "YOUARE") )
    {
        int index;
        sscanf(args[1], "%d", &index );
-       if ( (index >= 0) && (index < global->numPlayers) )
+       if ( (index >= 0) && (index < env.numGamePlayers) )
        {
-          global->client_player = global->players[index];
-          global->currTank = global->client_player->tank;
+          global.client_player = env.players[index];
+          global.set_curr_tank(global.client_player->tank);
        }
        return TRUE;
    }
@@ -366,39 +365,31 @@ int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer)
 
 
 
-
-void Create_Sky(ENVIRONMENT *env, GLOBALDATA *global)
+void Create_Sky()
 {
- if ( (env->custom_background) && (env->bitmap_filenames) )
-  {
-     if ( env->sky) destroy_bitmap(env->sky);
-     env->sky = load_bitmap( env->bitmap_filenames[ rand() % env->number_of_bitmaps ], NULL);
-  }
-
-    if ( (! env->custom_background) || (! env->sky) )
-    {
-      if ( env->sky ) destroy_bitmap(env->sky);
-#ifdef THREADS
-      if (env->get_waiting_sky())
-      {
-           env->sky = env->get_waiting_sky();
-           env->lock(env->waiting_sky_lock);
-           env->waiting_sky = NULL;
-           env->unlock(env->waiting_sky_lock);
-      }
-      else
-      {
-#endif
-      env->sky = create_bitmap( global->screenWidth, global->screenHeight - MENUHEIGHT);
-      generate_sky (global, env->sky, sky_gradients[global->cursky],
-                   (global->ditherGradients ? GENSKY_DITHERGRAD : 0 ) |
-                   (global->detailedSky ? GENSKY_DETAILED : 0 )  );
-#ifdef THREADS
-      }
-#endif
-  }
-
-}         // end of create sky function
+	if (env.custom_background && env.bitmap_filenames) {
+		if (env.sky)
+			destroy_bitmap(env.sky);
+		env.sky = load_bitmap(
+		       env.bitmap_filenames[ rand() % env.number_of_bitmaps ], nullptr);
+	}
+
+	if (!env.custom_background || !env.sky) {
+		if (env.sky
+		  && ( (env.sky->w  != env.screenWidth)
+		    || (env.sky->h != (env.screenHeight - MENUHEIGHT) ) ) ) {
+			destroy_bitmap(env.sky);
+			env.sky = nullptr;
+		}
+
+		if (!env.sky)
+			env.sky = create_bitmap(env.screenWidth,
+		                               env.screenHeight - MENUHEIGHT);
+		generate_sky (nullptr, sky_gradients[global.cursky],
+		                     (env.ditherGradients ? GENSKY_DITHERGRAD : 0 )
+		                   | (env.detailedSky ? GENSKY_DETAILED : 0 ) );
+	}
+} // end of create sky function
 
 
 
@@ -410,9 +401,11 @@ int Client_Fire(PLAYER *my_player, int my_socket)
    if (!my_player) return FALSE;
    if (! my_player->tank) return FALSE;
 
-   sprintf(buffer, "FIRE %d %d %d", my_player->tank->cw, my_player->tank->a,
-                   my_player->tank->p);
-   write(my_socket, buffer, strlen(buffer));
+   int32_t towrite, written;
+
+	SAFE_WRITE(my_socket, "FIRE %d %d %d", my_player->tank->cw,
+				my_player->tank->a, my_player->tank->p)
+
    return TRUE;
 }
 
@@ -457,7 +450,7 @@ int Client_Cycle_Weapon(PLAYER *my_player, int forward_or_back)
    {
         if (forward_or_back == CYCLE_FORWARD)
             my_player->tank->cw++;
-        else 
+        else
             my_player->tank->cw--;
 
         if (my_player->tank->cw >= THINGS)
@@ -488,28 +481,367 @@ int Client_Cycle_Weapon(PLAYER *my_player, int forward_or_back)
 // On success, a pointer to char is returned.
 // On failure, a NULL is returned.
 // The returned pointer does NOT need to be freed.
-char *Explain_Error(GLOBALDATA *global, int error_code)
+const char* Explain_Error(int32_t error_code)
 {
-    char *my_message = NULL;
+	switch (error_code) {
+		case CLIENT_ERROR_VERSION:
+			return env.ingame->Get_Line(77);
+			break;
+		case CLIENT_ERROR_SCREENSIZE:
+			return env.ingame->Get_Line(78);
+			break;
+		case CLIENT_ERROR_DISCONNECT:
+			return env.ingame->Get_Line(79);
+			break;
+	}
+
+	return nullptr;
+}
 
-    switch (error_code)
+
+// Client version of the game
+// Really, this loop should do some basic things.
+// 1. Find out what the landscape should look like from the server.
+// 2. Place tanks on the battle field
+// 3. Create missiles, beam weapons and such when the server asks us to
+// 4. Get input from the player and forward it to the server.
+// 5. Clean up at the end of the round.
+//
+// Function return TRUE if everything went well or FALSE
+// if an error occured.
+int Game_Client(int socket_number)
+{
+    int surface_x = 1, tank_position = 1, team_number = 1, name_number = 1;
+    int weapon_number = 1, item_number = 1, tank_health = 1;
+    int end_of_round = FALSE, keep_playing = FALSE;
+    int game_stage = CLIENT_VERSION;
+    char buffer[BUFFER_SIZE];
+    int incoming;
+    int my_key;
+    int time_clock = 0;
+    bool screen_update = false;
+    int count;      // generic counter
+    int stuff_going_down = FALSE;    // explosions, missiles etc on the screen
+    VIRTUAL_OBJECT *my_object, *next_obj;
+	int32_t class_ = 0;
+    bool fired = false;
+    int32_t towrite, written;
+
+
+    clear_to_color (global.terrain, PINK);    // get terrain ready
+    clear_to_color(global.canvas, BLACK);
+
+    // clean up old text
+	global.getHeadOfClass(CLASS_FLOATTEXT, &my_object);
+	while (my_object) {
+		my_object->getNext(&next_obj);
+		static_cast<FLOATTEXT*>(my_object)->newRound();
+		delete my_object;
+	}
+
+    Create_Sky();     // so we have a background
+	SAFE_WRITE(socket_number, "%s", "VERSION")
+
+    while (! end_of_round)
     {
-       case CLIENT_ERROR_VERSION:
-           my_message = global->ingame->complete_text[77];
-       break;
-       case CLIENT_ERROR_SCREENSIZE:
-           my_message = global->ingame->complete_text[78];
-       break;
-       case CLIENT_ERROR_DISCONNECT:
-           my_message = global->ingame->complete_text[79];
-       break;
-    }
+        // check for waiting input from the server
+        incoming = Check_For_Incoming_Data(socket_number);
+        if (incoming)
+        {
+           int bytes_read;
 
-    return my_message;
-}
+           memset(buffer, '\0', BUFFER_SIZE);
+           bytes_read = read(socket_number, buffer, BUFFER_SIZE);
+           if (bytes_read > 0)
+           {
+                // do something with this input
+                if (! strncmp(buffer, "CLOSE", 5) )
+                {
+                   end_of_round = TRUE;
+                   keep_playing = FALSE;
+                   printf("Got close message.\n");
+                   global.client_message = strdup(env.ingame->Get_Line(81));
+                }
+                else if (! strncmp(buffer, "NOROOM", 6) )
+                {
+                   end_of_round = TRUE;
+                   keep_playing = FALSE;
+                   printf("The server is full or the game has not started. Please try again later.\n");
+                   global.client_message = strdup(env.ingame->Get_Line(80));
+                }
+                else if (! strncmp(buffer, "GAMEEND", 7) )
+                {
+                    end_of_round = TRUE;
+                    keep_playing = FALSE;
+                    printf("The game is over.\n");
+                    if ( strlen(buffer) > 7)
+                        global.client_message = strdup(& (buffer[8])) ;
+                    else
+                        global.client_message = strdup(env.ingame->Get_Line(82));
+                }
+                else if (! strncmp(buffer, "ROUNDEND", 8) )
+                {
+                   end_of_round = TRUE;
+                   keep_playing = TRUE;
+                   printf("Round is over.\n");
+                }
+
+                else         // not a special command, parse it
+                {
+                  if ( Parse_Client_Data(buffer) )
+                  {
+                      if (game_stage < CLIENT_PLAYING)
+                         game_stage++;
+
+                      // Request more information
+                      if (game_stage < CLIENT_PLAYING)
+                      {
+                         switch (game_stage)
+                         {
+                            case CLIENT_SCREEN: strcpy(buffer, "SCREEN"); break;
+                            case CLIENT_WIND: strcpy(buffer, "WIND"); break;
+                            case CLIENT_NUMPLAYERS: strcpy(buffer, "NUMPLAYERS"); break;
+                            case CLIENT_TANK_POSITION: strcpy(buffer, "TANKPOSITION 0"); break;
+                            case CLIENT_SURFACE: strcpy(buffer, "SURFACE 0"); break;
+                            case CLIENT_WHOAMI: strcpy(buffer, "WHOAMI"); break;
+                            case CLIENT_WEAPONS: strcpy(buffer, "WEAPON 0"); break;
+                            case CLIENT_ITEMS:   strcpy(buffer, "ITEM 0"); break;
+                            case CLIENT_ROUNDS: strcpy(buffer, "ROUNDS"); break;
+                            case CLIENT_TEAMS: strcpy(buffer, "TEAMS 0");
+                                               global.updateMenu = TRUE; break;
+                            case CLIENT_WALL_TYPE: strcpy(buffer, "WALLTYPE"); break;
+                            case CLIENT_BOXED: strcpy(buffer, "BOXED"); break;
+                            case CLIENT_NAME: strcpy(buffer, "PLAYERNAME 0"); break;
+                            case CLIENT_TANK_HEALTH: strcpy(buffer, "HEALTH 0"); break;
+                            default: buffer[0] = '\0';
+                         }
+                         towrite = strlen(buffer);
+                         written = write(socket_number, buffer, strlen(buffer));
+                         if (written < towrite)
+							fprintf(stderr,
+								"%s:%d: Warning: Only %d/%d bytes sent to server\n",
+								__FILE__, __LINE__, written, towrite);
+                       }   // end of getting more info
+                   }    // our game stage went up
+                   else  // we got data, but our game stage did not go up
+                   {
+                       if (fired)
+                       {
+                           if ( (global.client_player) && (global.client_player->tank) )
+                           {
+                               fired = false;
+                               if (global.client_player->tank->cw < WEAPONS)
+								SAFE_WRITE(socket_number, "WEAPON %d",
+												global.client_player->tank->cw)
+                               else
+								SAFE_WRITE(socket_number, "ITEM %d",
+												global.client_player->tank->cw - WEAPONS)
+                           }
+                       }
+                       else if (game_stage == CLIENT_SURFACE)
+                       {
+								SAFE_WRITE(socket_number, "SURFACE %d", surface_x)
+                           surface_x++;
+                       }
+                       else if (game_stage == CLIENT_ITEMS)
+                       {
+								SAFE_WRITE(socket_number, "ITEM %d", item_number)
+                            item_number++;
+                       }
+                       else if (game_stage == CLIENT_TANK_POSITION)
+                       {
+								SAFE_WRITE(socket_number, "TANKPOSITION %d", tank_position)
+                            tank_position++;
+                            if (tank_position >= env.numGamePlayers)
+                              tank_position = 0;
+                       }
+                       else if (game_stage == CLIENT_TANK_HEALTH)
+                       {
+								SAFE_WRITE(socket_number, "HEALTH %d", tank_health)
+                            tank_health++;
+                            if (tank_health >= env.numGamePlayers)
+                                tank_health = 0;
+                       }
+                       else if (game_stage == CLIENT_TEAMS)
+                       {
+								SAFE_WRITE(socket_number, "TEAMS %d", team_number)
+                            team_number++;
+                       }
+                       else if (game_stage == CLIENT_NAME)
+                       {
+								SAFE_WRITE(socket_number, "PLAYERNAME %d", name_number)
+                            name_number++;
+                       }
+                       else if (game_stage == CLIENT_WEAPONS)
+                       {
+								SAFE_WRITE(socket_number, "WEAPON %d", weapon_number)
+                            weapon_number++;
+                       }
+                       else if (game_stage == CLIENT_PLAYING)
+                       {
+                           time_clock++;
+                           if (time_clock > 1)   // check positions every few inputs
+                           {
+                               time_clock = 0;
+                               if (surface_x < env.screenWidth)
+                               {
+                                   game_stage = CLIENT_SURFACE;
+								SAFE_WRITE(socket_number, "SURFACE %d", surface_x)
+                                   surface_x++;
+                               }
+                               else
+                               {
+                                 game_stage = CLIENT_TANK_POSITION;
+                                 tank_position = 1;
+								SAFE_WRITE(socket_number, "TANKPOSITION %d", 0)
+                               }    // game stage stuff
+                           }
+                       }        // end of playing commands
+                   }
+
+                }      // end of we got something besides the close command
+
+           }
+           else    // connection was broken
+           {
+              close(socket_number);
+              printf("Server closed connection.\n");
+              end_of_round = TRUE;
+           }
+        }
+
+		class_ = 0;
+		while (class_ < CLASS_COUNT) {
+			if (CLASS_TANK == class_) {
+				++class_;
+				continue;
+			}
+
+			global.getHeadOfClass(static_cast<eClasses>(class_), &my_object);
+			while(my_object) {
+				my_object->getNext(&next_obj);
+
+				if (CLASS_EXPLOSION == class_)
+					static_cast<EXPLOSION*>(my_object)->explode();
+
+				my_object->applyPhysics();
+
+				if (my_object->destroy) {
+					my_object->requireUpdate();
+					my_object->update();
+					delete my_object;
+					if (CLASS_TELEPORT == class_)
+						time_clock = 2;
+				}
+
+				if ( (CLASS_BEAM      == class_)
+				  || (CLASS_MISSILE   == class_)
+				  || (CLASS_EXPLOSION == class_)
+				  || (CLASS_TELEPORT  == class_) )
+					stuff_going_down = TRUE;
+
+				my_object = next_obj;
+			}
+			++class_;
+		}
+
+		global.slideLand();
+
+        // update everything on the screen
+        if (global.updateMenu)
+          draw_top_bar ();
+
+        if (screen_update) {
+			screen_update = false;
+			global.make_fullUpdate();
+        }
+		global.replace_canvas ();
+
+        screen_update = true;
+
+		class_ = 0;
+		while (class_ < CLASS_COUNT) {
+			global.getHeadOfClass(static_cast<eClasses>(class_), &my_object);
+			while(my_object) {
+				my_object->draw();
+				if (CLASS_FLOATTEXT == class_)
+					my_object->requireUpdate();
+				my_object->update();
+				my_object->getNext(&my_object);
+			}
+			++class_;
+		}
+
+        global.do_updates();
+
+
+        // check for input from the user
+        if ( keypressed() )
+        {
+           my_key = readkey();
+           my_key = my_key >> 8;
+           if (my_key == KEY_SPACE)
+           {
+              Client_Fire(global.client_player, socket_number);
+              fired = true;
+           }
+           else if (my_key == KEY_ESC)
+           {
+              end_of_round = TRUE;
+              close(socket_number);
+           }
+           else if (my_key == KEY_UP)
+           {
+              Client_Power(global.client_player, CLIENT_UP);
+           }
+           else if (my_key == KEY_DOWN)
+           {
+              Client_Power(global.client_player, CLIENT_DOWN);
+           }
+           else if (my_key == KEY_LEFT)
+           {
+              Client_Angle(global.client_player, CLIENT_LEFT);
+           }
+           else if (my_key == KEY_RIGHT)
+           {
+              Client_Angle(global.client_player, CLIENT_RIGHT);
+           }
+           else if ( (my_key == KEY_Z) || (my_key == KEY_BACKSPACE) )
+           {
+               Client_Cycle_Weapon(global.client_player, CYCLE_BACK);
+           }
+           else if ( (my_key == KEY_C) || (my_key == KEY_TAB) )
+           {
+               Client_Cycle_Weapon(global.client_player, CYCLE_FORWARD);
+               global.updateMenu = TRUE;
+           }
 
+           screen_update = false;
+           global.updateMenu = TRUE;
+        }
 
+        // pause for a moment
+        // if (game_stage < CLIENT_PLAYING)
+        if (stuff_going_down)
+        {
+            LINUX_SLEEP;
+            stuff_going_down = FALSE;
+        }
+    }
+
+    // we should clean up here
+    for (count = 0; count < env.numGamePlayers; count++)
+    {
+       if (env.players[count]->tank)
+       {
+           delete env.players[count]->tank;
+           env.players[count]->tank = NULL;
+       }
+    }
+
+    return keep_playing;
+}
 
 
-#endif
+#endif // NETWORK
 
diff --git a/src/client.h b/src/client.h
index 488ed37..9c2946f 100644
--- a/src/client.h
+++ b/src/client.h
@@ -1,9 +1,6 @@
 #ifndef CLIENT_HEADER_FILE__
 #define CLIENT_HEADER_FILE__
 
-#include "globaldata.h"
-#include "environment.h"
-
 #ifdef NETWORK
 
 #define CLIENT_VERSION 1
@@ -42,26 +39,25 @@
 // This function takes some data from the server
 // and tries to figure out what to do with it.
 // The game stage is returned.
-int Parse_Client_Data(GLOBALDATA *global, ENVIRONMENT *env, char *buffer);
+int Parse_Client_Data(char *buffer);
 
 
 // Draws a background
-void Create_Sky(ENVIRONMENT *env, GLOBALDATA *global);
+void Create_Sky();
 
 // Sends fire command to the server
 // Message must be in format "FIRE item angle power"
 int Client_Fire(PLAYER *my_player, int my_socket);
-
-
 int Client_Power(PLAYER *my_player, int up_or_down);
-
 int Client_Angle(PLAYER *my_player, int left_or_right);
-
 int Client_Cycle_Weapon(PLAYER *my_player, int forward_or_back);
 
 // Take an error code and return a string with readable info.
 // The returning string should NOT be freed after use.
-char *Explain_Error(GLOBALDATA *global, int error_code);
+// Note: This is nowhere used. ( REMOVEME ??? )
+const char* Explain_Error(int32_t error_code);
+
+int Game_Client(int socket_number);
 
 #endif
 
diff --git a/src/clock.cpp b/src/clock.cpp
new file mode 100644
index 0000000..e733ad2
--- /dev/null
+++ b/src/clock.cpp
@@ -0,0 +1,66 @@
+#include "clock.h"
+
+#include <chrono>
+
+/// === Used clocks and time granularity ===
+using atanks_clock_t = std::chrono::steady_clock;
+
+#if defined(ATANKS_IS_MSVC) && !defined(ATANKS_IS_AT_LEAST_MSVC13)
+  // Note: this is a bug in vc12, that is fixed in vc13.
+  // See: https://connect.microsoft.com/VisualStudio/feedback/details/858357/steady-clock-now-returning-the-wrong-type
+# include "winclock.h"
+  using time_point_t = std::chrono::time_point<std::chrono::system_clock>;
+#else
+  using time_point_t = std::chrono::time_point<atanks_clock_t>;
+#endif // MSVC++ 2013 bug
+
+using clock_ms_t = std::chrono::milliseconds;
+using clock_us_t = std::chrono::microseconds;
+
+/// === Helper macros to not have ridiculously long lines ===
+#define CLOCK_NOW  atanks_clock_t::now()
+#define MS_CAST(x) static_cast<int32_t>(std::chrono::duration_cast<clock_ms_t>(x).count())
+#define US_CAST(x) static_cast<int32_t>(std::chrono::duration_cast<clock_us_t>(x).count())
+
+/// === Internal values only used here ===
+static time_point_t game_us_end   = CLOCK_NOW;
+static time_point_t game_us_start = CLOCK_NOW;
+static time_point_t menu_ms_end   = CLOCK_NOW;
+static time_point_t menu_ms_start = CLOCK_NOW;
+
+
+/// === Function implementations ===
+
+/// REMOVE_VS12_WORKAROUND
+#if !defined(ATANKS_IS_MSVC) || defined(ATANKS_IS_AT_LEAST_MSVC13)
+int32_t game_us_get()
+{
+	game_us_end     = CLOCK_NOW;
+	int32_t used_us = US_CAST(game_us_end - game_us_start);
+	game_us_start   = game_us_end;
+	return used_us > 0 ? used_us : 0;
+}
+
+
+void game_us_reset()
+{
+	game_us_end   = CLOCK_NOW;
+	game_us_start = game_us_end;
+}
+
+
+int32_t menu_ms_get()
+{
+	menu_ms_end     = CLOCK_NOW;
+	int32_t used_us = MS_CAST(menu_ms_end - menu_ms_start);
+	menu_ms_start   = menu_ms_end;
+	return used_us > 0 ? used_us : 0;
+}
+
+
+void menu_ms_reset()
+{
+	menu_ms_end   = CLOCK_NOW;
+	menu_ms_start = menu_ms_end;
+}
+#endif // !ATANKS_IS_MSVC
diff --git a/src/clock.h b/src/clock.h
new file mode 100644
index 0000000..bbf4e7b
--- /dev/null
+++ b/src/clock.h
@@ -0,0 +1,25 @@
+#pragma once
+#ifndef ATANKS_SRC_CLOCK_H_INCLUDED
+#define ATANKS_SRC_CLOCK_H_INCLUDED
+
+#include "debug.h"
+#include <cstdint>
+
+int32_t game_us_get();
+void    game_us_reset();
+int32_t menu_ms_get();
+void    menu_ms_reset();
+
+/// REMOVE_VS12_WORKAROUND
+#if defined(ATANKS_IS_MSVC) && !defined(ATANKS_IS_AT_LEAST_MSVC13)
+void win_clock_deinit();
+void win_clock_init();
+#define WIN_CLOCK_INIT   win_clock_init();
+#define WIN_CLOCK_REMOVE win_clock_deinit();
+#else
+#define WIN_CLOCK_INIT {}
+#define WIN_CLOCK_REMOVE {}
+#endif
+
+#endif // ATANKS_SRC_CLOCK_H_INCLUDED
+
diff --git a/src/debris_pool.cpp b/src/debris_pool.cpp
new file mode 100644
index 0000000..667a171
--- /dev/null
+++ b/src/debris_pool.cpp
@@ -0,0 +1,238 @@
+#include "debris_pool.h"
+#include "main.h"
+
+#include <cassert>
+
+/// @brief sDebrisItem default constructor
+sDebrisItem::sDebrisItem(int32_t diameter_, sDebrisItem* next_) :
+	idx( (diameter_ / 2) - 1),
+	next(next_)
+{
+	if (diameter_ && (idx >= 0) && (idx < 5)) {
+		bmp = create_bitmap(diameter_ + 1, diameter_ + 1);
+	}
+
+	if (bmp && next) {
+		prev = next->prev;
+		next->prev = this;
+		if (prev)
+			prev->next = this;
+	}
+
+	if (bmp) {
+		clear_to_color(bmp, PINK);
+	} else
+		// unusable...
+		delete this;
+}
+
+
+/// @brief take this out of the list and free bmp data
+sDebrisItem::~sDebrisItem()
+{
+	if (bmp)
+		destroy_bitmap(bmp);
+	bmp = nullptr;
+
+	if (prev)
+		prev->next = next;
+	if (next)
+		next->prev = prev;
+	prev = nullptr;
+	next = nullptr;
+}
+
+
+/// @brief create an empty pool with a set limit
+sDebrisPool::sDebrisPool(int32_t limit_) :
+	limit(limit_ * 5) // The limit is per debris size
+{
+	DEBUG_LOG_OBJ("Debris Pool", "Pool created with limit %d", limit);
+
+	// memset initialization, Visual C++ 2013 can not do array initialization
+	memset(avail,  0, sizeof(int32_t) * 5);
+	memset(counts, 0, sizeof(int32_t) * 5);
+	memset(heads,  0, sizeof(item_t*) * 5);
+	memset(tails,  0, sizeof(item_t*) * 5);
+
+	// Pre-create a third of the possible pool elements now,
+	// so an evenly distributed base is given at any time.
+	int32_t max_items = limit / 3 / 5;
+
+	for (int32_t r = 1; r < 6; ++r) {
+		for (int32_t i = 0; i < max_items; ++i)
+			create_item(r);
+	}
+}
+
+
+/// @brief destroy all pool items
+sDebrisPool::~sDebrisPool()
+{
+	for (int32_t r = 0; r < 5; ++r) {
+		while (heads[r]) {
+			item_t* curr = heads[r];
+			heads[r] = curr->next;
+			delete curr;
+			--counts[r];
+			--count_all;
+		}
+
+		if (counts[r]) {
+			cerr << "ERROR: debris pool count for radius " << (r + 1);
+			cerr << " should be zero but is " << counts[r] << " !" << endl;
+		}
+	}
+	if (count_all) {
+		cerr << "ERROR: total debris pool count should be zero but is ";
+		cerr << count_all << " !" << endl;
+	}
+}
+
+
+/// @brief centralized creation, so it can be used by both the ctor and get_item()
+sDebrisItem* sDebrisPool::create_item(int32_t radius)
+{
+	item_t* res = nullptr;
+
+	if (count_all < limit) {
+
+		int32_t idx = radius - 1;
+
+		res = new sDebrisItem(radius * 2, heads[idx]);
+
+		if (res) {
+			// Insert item as new head
+			heads[idx] = res;
+			if (nullptr == tails[idx])
+				tails[idx] = res;
+
+			// Count the item
+			++count_all;
+			++counts[idx];
+			++avail[idx];
+			DEBUG_LOG_OBJ("Debris", "New maximum number: %d", count_all)
+		}
+
+	}
+
+	return res;
+}
+
+
+/** @brief mark one item as unused.
+  * Do not do this yourself, although it is a struct.
+  * The pool needs to keep track of how many items are available.
+**/
+void sDebrisPool::free_item(item_t* item)
+{
+	if (item && !item->is_free) {
+		// Reset item to PINK
+		if (item->bmp)
+			clear_to_color(item->bmp, PINK);
+
+		// Mark item as being free
+		item->is_free = true;
+		++avail[item->idx];
+	}
+}
+
+
+/** @brief get the next free item with the specified radius.
+  *
+  * If no item is available, a new one will be created unless the
+  * current limit is reached.
+**/
+sDebrisItem* sDebrisPool::get_item (int32_t radius)
+{
+	assert( (radius > 0) && (radius < 6)
+		&& "ERROR: Only radius in the range [1;5] supported!");
+
+	if ( (radius < 1) || (radius > 5) )
+		return nullptr;
+
+	int32_t idx = radius - 1;
+
+	// See whether an item is available:
+	if (avail[idx]) {
+		item_t* curr = heads[idx];
+		while (curr && !curr->is_free)
+			curr = curr->next;
+
+		assert(curr && "ERROR: avail[idx] is not 0, but no item available!");
+
+		if (curr) {
+			--avail[idx];
+			curr->is_free = false;
+			return curr;
+		} else {
+			cerr << "No item for radius " << radius << " found, but ";
+			cerr << avail[idx] << " should be available!" << endl;
+			avail[idx] = 0;
+		}
+	}
+
+	/* --- If this failed, or no items are available, create a new item --- */
+
+	// Check whether the limit is hit already
+	if (count_all >= limit) {
+		/* If the limit is hit, this is the strategy:
+		 * a) look which index has the most items
+		 * b) Take the first free item and delete it
+		 * -> Voilà, another item is available!
+		 * <- no item free? Doesn't matter, we are busy now!
+		 * ( Note : If the list with the most items has none free,
+		 *          then it is safe to assume that none has. )
+		 */
+
+		DEBUG_LOG_OBJ("Debris Limit", "The limit of %d was reached", limit)
+
+		int32_t hasMaxIdx = 0;
+
+		for (int32_t i = 0; i < 5; ++i) {
+			if ( (i != idx)
+			  && avail[i]
+			  && ( (counts[i] > counts[hasMaxIdx])
+				|| !avail[hasMaxIdx]) )
+				hasMaxIdx = i;
+		}
+
+		if (avail[hasMaxIdx]) {
+			item_t* curr = heads[hasMaxIdx];
+			while (curr && !curr->is_free)
+				curr = curr->next;
+
+			// If a free item is found, delete it!
+			if (curr) {
+				// fix head/tail if affected
+				if (heads[hasMaxIdx] == curr)
+					heads[hasMaxIdx] = curr->next;
+				if (tails[hasMaxIdx] == curr)
+					tails[hasMaxIdx] = curr->prev;
+				delete curr;
+				--avail[hasMaxIdx];
+				--counts[hasMaxIdx];
+				--count_all;
+			} else {
+				cerr << "No item for radius " << (hasMaxIdx + 1) << " found, but ";
+				cerr << avail[hasMaxIdx] << " should be available!" << endl;
+				avail[hasMaxIdx] = 0;
+			}
+		}
+
+	} // End of limit reached resolving strategy
+
+	// If the limit is not reached (or relieved) at this point,
+	// a new item can be created
+	item_t* new_item = create_item(radius);
+
+	if (new_item) {
+		// Note: create_item() has already raised counts[idx],
+		//       count_all and avail[idx].
+		--avail[idx];
+		new_item->is_free = false;
+	}
+
+	return new_item;
+}
+
diff --git a/src/debris_pool.h b/src/debris_pool.h
new file mode 100644
index 0000000..f4df087
--- /dev/null
+++ b/src/debris_pool.h
@@ -0,0 +1,73 @@
+#pragma once
+#ifndef ATANKS_SRC_DEBRIS_POOL_H_INCLUDED
+#define ATANKS_SRC_DEBRIS_POOL_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ * Copyright (C) 2003  Thomas Hudson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "main.h"
+
+/** @struct sDebrisItem
+  * @brief represent one entry in the debris pool
+**/
+struct sDebrisItem
+{
+    BITMAP*      bmp      = nullptr;
+	int32_t      idx      = 0;       //!< calculated index from diameter (aka radius/2-1)
+    bool         is_free  = true;
+    sDebrisItem* next     = nullptr;
+    sDebrisItem* prev     = nullptr;
+
+	explicit sDebrisItem(int32_t diameter_, sDebrisItem* next_);
+	~sDebrisItem();
+};
+
+
+/** @struct sDebrisPool
+  * @brief A pool of bitmaps used to throw around dirt.
+  *
+  * Note: Currently the pool is limited to radius [1;5] the
+  *       debris can have. That is five series of bitmaps.
+**/
+struct sDebrisPool
+{
+	typedef sDebrisItem item_t;
+
+	explicit sDebrisPool(int32_t limit_);
+	~sDebrisPool();
+
+	void    free_item(item_t* item);
+	item_t* get_item (int32_t radius);
+
+private:
+
+	item_t* create_item(int32_t radius);
+
+	int32_t avail[5];      //!< How many items are available for which radius.
+	int32_t count_all = 0; //!< Sum of all created items.
+	int32_t counts[5];     //!< How many items are used for which radius.
+	int32_t limit     = 0; //!< The limit of the pool, set on pool creation.
+	item_t* heads[5];
+	item_t* tails[5];
+};
+
+
+#endif // ATANKS_SRC_DEBRIS_POOL_H_INCLUDED
+
diff --git a/src/debug.cpp b/src/debug.cpp
new file mode 100644
index 0000000..0499a11
--- /dev/null
+++ b/src/debug.cpp
@@ -0,0 +1,64 @@
+#include "debug.h"
+
+#include <ctime>
+#include <cstdarg>
+#include <iostream>
+#include <mutex>
+
+#if defined(ATANKS_DEBUG)
+
+#if defined(ATANKS_IS_WINDOWS) || defined(ATANKS_DEBUG_LOGTOFILE)
+# include <cstdio>
+#endif
+
+
+// Log Mutex
+std::mutex log_lock;
+
+
+/// @brief use with DEBUG_LOG to get debug dependent positional information for free!
+void debug_log(const char* moduleName, const char* title, const char* message, ...)
+{
+	char timebuf[21];
+	time_t t;
+	struct tm tm_;
+	char xMsg[512];
+
+	// Create timestamp
+	atanks_tzset();
+	t   = time(NULL);
+	atanks_localtime(&tm_, &t);
+	atanks_snprintf(timebuf, 20, "%04d.%02d.%02d %02d:%02d:%02d",
+				tm_.tm_year + 1900, tm_.tm_mon + 1, tm_.tm_mday,
+				tm_.tm_hour, tm_.tm_min, tm_.tm_sec);
+	timebuf[20] = 0x0;
+
+	// Create message
+	va_list vl;
+	va_start(vl, message);
+	vsprintf_s(xMsg, 512, message, vl);
+	va_end(vl);
+
+	log_lock.lock();
+
+#if defined(ATANKS_IS_WINDOWS) || defined(ATANKS_DEBUG_LOGTOFILE)
+	// Unfortunately, for everything to work right,
+	// a WinApp must be created. So write the log msg
+	// to atanks.log instead.
+	FILE* out = fopen("atanks.log", "a");
+	if (out) {
+		fprintf(out, "%s : %s : \"%s\" - %s\n",
+		        timebuf, moduleName, title, xMsg);
+		fclose(out);
+	}
+#endif // MSVC or explicit logging to atanks.log
+#if !defined(ATANKS_IS_WINDOWS)
+	fprintf(stdout, "%s : %s : \"%s\" - %s\n",
+	        timebuf, moduleName, title, xMsg);
+#endif // !Windows
+
+
+	log_lock.unlock();
+}
+
+#endif
diff --git a/src/debug.h b/src/debug.h
new file mode 100644
index 0000000..d86b1df
--- /dev/null
+++ b/src/debug.h
@@ -0,0 +1,168 @@
+#pragma once
+#ifndef ATANKS_SRC_DEBUG_H_INCLUDED
+#define ATANKS_SRC_DEBUG_H_INCLUDED
+
+#include <cstdio>
+
+/********************************************************
+ * Determine whether we build for BSD, Linux or Windows *
+ *******************************************************/
+#if defined(_WIN32) || defined(__WIN32__)
+# define ATANKS_IS_WINDOWS
+# if defined(_MSC_VER)
+#   define ATANKS_IS_MSVC
+    // See whether the chrono bug is fixed:
+#   if (_MSC_VER >= 1900)
+#     define ATANKS_IS_AT_LEAST_MSVC13
+#   endif // VS 2015
+# endif // _MSVC_VER
+#endif // Win 32
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
+# define ATANKS_IS_BSD
+#endif // BSD
+
+#if defined(__linux__) || defined(__linux) || defined(linux) || defined(ATANKS_IS_BSD)
+# define ATANKS_IS_LINUX
+#endif // Linux
+
+#if !defined(ATANKS_IS_WINDOWS) && !defined(ATANKS_IS_LINUX)
+# error "Only Windows, Linux and BSD are supported at the moment"
+#endif // Windows versus Linux
+
+
+/**********************************************************
+ * Define some macros to work around compiler differences *
+ *********************************************************/
+#if defined(ATANKS_IS_MSVC)
+# define atanks_snprintf(target, char_count, fmt, ...) \
+    _snprintf_s(target, char_count + 1, char_count, fmt, __VA_ARGS__)
+# define atanks_tzset() _tzset()
+# define atanks_localtime(tm_p, time_p) localtime_s(tm_p, time_p)
+#else
+# define atanks_snprintf(target, char_count, fmt, ...) \
+    snprintf(target, char_count, fmt, __VA_ARGS__)
+# define atanks_tzset() tzset()
+# define atanks_localtime(tm_p, time_p) localtime_r(time_p, tm_p)
+# define vsprintf_s(tgt, size, fmt, listvar) vsprintf(tgt, fmt, listvar)
+#endif // MSVC++ versus GCC/Clang
+
+
+// Windows special definitions
+#if defined(ATANKS_IS_WINDOWS)
+# ifndef WIN32_LEAN_AND_MEAN
+#   define WIN32_LEAN_AND_MEAN
+# endif
+
+# ifndef WIN32_EXTRA_LEAN
+#   define WIN32_EXTRA_LEAN
+# endif
+
+# ifndef VC_EXTRALEAN
+#   define VC_EXTRALEAN
+# endif
+
+# define NOMINMAX
+
+#endif // IS_WINDOWS
+
+
+/// ==============================================================
+/// === The following is only used if ATANKS_DEBUG is defined. ===
+/// === Otherwise the macro DEBUG_LOG() does nothing.          ===
+/// ==============================================================
+
+
+#if defined(ATANKS_DEBUG)
+
+// ATANKS_GET_FILE - This macro simply sets target to the current filename without a path
+#ifdef ATANKS_IS_MSVC
+#  define ATANKS_GET_FILE(target) { \
+     char _atanks_fname_info[64]; \
+     char _atanks_extension[8]; \
+     _splitpath_s(__FILE__, NULL, 0, NULL, 0, _atanks_fname_info, 63, _atanks_extension, 7); \
+     atanks_snprintf(target, 255, "%s%s", _atanks_fname_info, \
+               _atanks_extension); \
+   }
+#else
+#  define ATANKS_GET_FILE(target) { \
+     atanks_snprintf(target, 255, "%s", basename(__FILE__)); \
+   }
+#endif
+
+
+// ATANKS_GET_POS - This macro gathers positional information
+#ifdef ATANKS_IS_MSVC
+#  define ATANKS_GET_POS(target) { \
+     ATANKS_GET_FILE(target) \
+     atanks_snprintf(target, 255, "%-10s:%4d %-24s", target, __LINE__, __FUNCTION__); \
+   }
+#else
+#  define ATANKS_GET_POS(target) { \
+     atanks_snprintf(target, 255, "%-10s:%4d %-24s", basename(__FILE__), __LINE__, __FUNCTION__); \
+   }
+#endif
+
+
+// DEBUG_LOG - This macro is a wrapper for debug_log
+#define DEBUG_LOG(Title, Msg, ...) { \
+   char _atanks_trace_info[256]; \
+   ATANKS_GET_POS(_atanks_trace_info) \
+   debug_log(_atanks_trace_info, Title, Msg, __VA_ARGS__); \
+ }
+
+// declaration if debug_log
+void debug_log(const char* moduleName, const char* title, const char* message, ...);
+
+
+#else
+
+#define DEBUG_LOG(...) {}
+
+#endif // defined(ATANKS_DEBUG)
+
+// Enable / Disable specific debug message flavours
+// Those do nothing, even if enabled, if ATANKS_DEBUG is not defined
+
+#ifdef ATANKS_DEBUG_AIMING
+#  define DEBUG_LOG_AIM(Title, Msg, ...) DEBUG_LOG(Title, Msg, __VA_ARGS__)
+#else
+#  define DEBUG_LOG_AIM(...) {}
+#endif // ATANKS_DEBUG_AIMING
+
+#ifdef ATANKS_DEBUG_EMOTIONS
+#  define DEBUG_LOG_EMO(Title, Msg, ...) DEBUG_LOG(Title, Msg, __VA_ARGS__)
+#else
+#  define DEBUG_LOG_EMO(...) {}
+#endif // ATANKS_DEBUG_EMOTIONS
+
+// The next is a helper so those messages can show up whenever
+// either aiming or emotions shall be logged.
+#if defined(ATANKS_DEBUG_AIMING) || defined(ATANKS_DEBUG_EMOTIONS)
+#  define DEBUG_LOG_AI(Title, Msg, ...) DEBUG_LOG(Title, Msg, __VA_ARGS__)
+#else
+#  define DEBUG_LOG_AI(...) {}
+#endif // ATANKS_DEBUG_AIMING || ATANKS_DEBUG_EMOTIONS
+
+
+#ifdef ATANKS_DEBUG_FINANCE
+#  define DEBUG_LOG_FIN(Title, Msg, ...) DEBUG_LOG(Title, Msg, __VA_ARGS__)
+#else
+#  define DEBUG_LOG_FIN(...) {}
+#endif // ATANKS_DEBUG_FINANCE
+
+#ifdef ATANKS_DEBUG_OBJECTS
+#  define DEBUG_LOG_OBJ(Title, Msg, ...) DEBUG_LOG(Title, Msg, __VA_ARGS__)
+#else
+#  define DEBUG_LOG_OBJ(...) {}
+#endif // ATANKS_DEBUG_OBJECTS
+
+#ifdef ATANKS_DEBUG_PHYSICS
+#  define DEBUG_LOG_PHY(Title, Msg, ...) DEBUG_LOG(Title, Msg, __VA_ARGS__)
+#else
+#  define DEBUG_LOG_PHY(...) {}
+#endif // ATANKS_DEBUG_OBJECTS
+
+
+#endif // ATANKS_SRC_DEBUG_H_INCLUDED
+
diff --git a/src/decor.cpp b/src/decor.cpp
new file mode 100644
index 0000000..ee37d46
--- /dev/null
+++ b/src/decor.cpp
@@ -0,0 +1,441 @@
+#include "decor.h"
+#include "sound.h"
+#include "tank.h"
+
+#include <cassert>
+
+/// @brief Default constructor
+DECOR::DECOR(double x_, double y_, double xv_, double yv_,
+			int32_t maxRadius, int32_t type_, int32_t delay_) :
+	PHYSICAL_OBJECT(false),
+	curWind(global.wind),
+	delay(delay_),
+	maxGravAccel(-4. * env.gravity * env.FPS_mod),
+	maxWind(env.windstrength),
+	maxWindAccel(global.wind * env.FPS_mod),
+	radius(maxRadius),
+	type(type_)
+{
+	x = x_;
+	y = y_;
+	xv = xv_;
+	yv = yv_;
+
+	if (DECOR_DIRT == type) {
+		// The core data is taken from the meteors.
+		weapType = SML_METEOR
+		         + (maxRadius / 2); // results in (0, 1, 1, 2, 2) for radius [1;5]
+		mass = naturals[weapType - WEAPONS].mass;
+		drag = naturals[weapType - WEAPONS].drag / 5.;
+
+		// Special physics for dirt debris:
+        physType = PT_DIRTBOUNCE;
+
+        // Only keep dirt alive while it is really moving,
+        // if it becomes too slow, only keep it for 2 seconds
+        maxAge = 2 * env.frames_per_second;
+
+        // The diameter is just used so it does not have to
+        // be calculated each time updateDirt() is called.
+        diameter = radius * 2;
+
+        // Calculate how many pixels are needed per call to updateDirt()
+        grabPerCall = ((diameter + 1) * (diameter + 1)) / (delay > 1 ? delay : 1);
+	} else if (DECOR_SMOKE == type) {
+		int32_t tempCol = 128 + (rand () % 64);
+
+		if (maxRadius <= 3)
+			radius = 3;
+		else
+			radius = 3 + (rand () % (maxRadius - 2));
+
+		color  = makecol (tempCol, tempCol, tempCol);
+		mass   = 1.0 + (static_cast<double>(rand () %  5) /  10.);
+		drag   = 0.9 + (static_cast<double>(rand () % 90) / 100.);
+
+		// maximum age depends on the maximum radius and the real radius,
+		// plus 0 to 2 extra seconds.
+		maxAge  = ( (maxRadius - (maxRadius - radius)) / 3) + (rand() % 3);
+		maxAge *= env.frames_per_second;
+
+		// Special physics for smoke, only for repulsion
+		physType = PT_SMOKE;
+
+		// Smoke does not need the dirt grabber
+		ready = true;
+	} else
+		destroy = true;
+
+	maxVel = env.maxVelocity * (1.20 + (mass / (.01 * MAX_POWER)));
+
+	// Add to the chain:
+	global.addObject(this);
+}
+
+
+/// @brief Constructor with bitmap
+DECOR::DECOR(double x_, double y_, double xv_, double yv_,
+			int32_t maxRadius, int32_t type_, int32_t delay_,
+			sDebrisItem* deb_item, sDebrisItem* met_item) :
+	DECOR(x_, y_, xv_, yv_, maxRadius, type_, delay_)
+{
+	// Everything done in delegated ctor, only img to set
+	dirt   = deb_item;
+	setBitmap(dirt ? dirt->bmp : nullptr);
+	// Note: It is safe to distribute dirt->bmp, because bitmap normally holds
+	// global graphics and must not be destroyed.
+	meteor = met_item;
+
+	if ( (nullptr == dirt) || !hasBitmap() )
+		// Can't work without...
+		destroy = true;
+}
+
+
+/// @brief default destructor
+DECOR::~DECOR()
+{
+	if (DECOR_DIRT == type) {
+		// Draw dirt on terrain and add land slide
+		rotate_sprite (global.terrain, dirt->bmp, x - radius, y - radius, itofix (angle));
+		global.addLandSlide(x - radius - 1, x + radius + 1, false);
+	}
+
+	if (dirt) {
+		global.free_debris_item(dirt);
+		dirt = nullptr;
+	}
+
+	if (meteor) {
+		global.free_debris_item(meteor);
+		meteor = nullptr;
+	}
+
+	// Update the last drawing area
+	int32_t calcRadius = radius;
+
+	if (DECOR_DIRT == type)
+			++calcRadius;
+	else if (DECOR_SMOKE == type)
+		// The older, the larger...
+		calcRadius = static_cast<int32_t>(radius * (4.0 * age / maxAge));
+
+	setUpdateArea ( x - calcRadius - 1, y - calcRadius - 1,
+					(calcRadius * 2) + 2, (calcRadius * 2) + 2);
+	requireUpdate ();
+	this->update();
+
+	// Take out of the chain:
+	global.removeObject(this);
+}
+
+
+/// @brief let smoke drift and disperse with the wind
+void DECOR::applyPhysics ()
+{
+	if (destroy)
+		return;
+
+	if (delay > 0) {
+		--delay;
+		return;
+	}
+
+	// for detecting bounces
+	double old_yv = yv;
+
+	if (DECOR_DIRT == type) {
+
+		// Check whether movement ended
+		double movement = FABSDISTANCE2(xv, yv, 0, 0);
+		bool   on_floor = isOnFloor(); // Needed again below.
+
+		if ( on_floor
+		  && ( (hitSomething && (movement < 0.8))
+		    || (movement < 0.2) ) )  {
+
+			// It ended!
+
+			// fix y:
+			int32_t dirt_bottom = y + dirt->bmp->h;
+			if ( ( (y - radius) > MENUHEIGHT)
+			  && ( dirt_bottom < env.screenHeight)
+			  && (PINK != getpixel(global.terrain, x, dirt_bottom)) )
+				--y;
+
+			xv = 0.;
+			yv = 0.;
+			destroy = true;
+			requireUpdate();
+
+		} else {
+			hitSomething = false; // Enable checking.
+
+			// Now apply physics
+			repulseDecor();
+			PHYSICAL_OBJECT::applyPhysics();
+
+			// Be sure x/y values are sane (Can drift into walls
+			// on rare wind conditions.)
+			if (x < 2) x = 2;
+			if (x > (env.screenWidth  - 2)) x = env.screenWidth  - 2;
+			if (y > (env.screenHeight - 2)) y = env.screenHeight - 2;
+
+			// Maybe play a sound on bounce
+			if ( !global.skippingComputerPlay
+			  && (old_yv > .5)
+			  && (yv < -0.1) )
+				play_natural_sound(DIRT_FRAGMENT, x, radius * 32,
+						1200 - (radius * 50));
+		}
+
+		// raise age if movement is below 0.5
+		if ( ( on_floor || (FABSDISTANCE2(xv, yv, 0, 0) < .5) )
+		  && (++age > maxAge) )
+			destroy = true;
+
+	} else if (DECOR_SMOKE == type) {
+		// Apply wind first
+		int32_t ageMod  = ROUND(std::abs(curWind / (maxWind / 2.0))) + 1;
+
+		/* This produces: (with max wind = 8)
+		 * wind = 0 : round(0 / (8 / 2)) + 1 = round(0 / 4) + 1 = 0 + 1 = 1 <-- normal aging
+		 * wind = 1 : round(1 / (8 / 2)) + 1 = round(1 / 4) + 1 = 0 + 1 = 1 <-- normal aging
+		 * wind = 4 : round(4 / (8 / 2)) + 1 = round(4 / 4) + 1 = 1 + 1 = 2 <-- raised aging
+		 * wind = 6 : round(6 / (8 / 2)) + 1 = round(6 / 4) + 1 = 2 + 1 = 3 <-- fast aging
+		 * wind = 8 : round(8 / (8 / 2)) + 1 = round(8 / 4) + 1 = 2 + 1 = 3 <-- fast aging
+		*/
+		age += ageMod;
+
+		// Set further values
+		// Try to reach half distance to the maximum values per second
+		double xaccel = ((xv + maxWindAccel) / 2)
+		              / static_cast<double>(env.frames_per_second);
+		double yaccel = ((yv + maxGravAccel) / 2)
+		              / static_cast<double>(env.frames_per_second / 10.);
+
+		// Apply current acceleration
+		xv += xaccel;
+		yv += yaccel;
+
+		// Add repulsion:
+		repulseDecor();
+
+		// Be sure that neither xv outruns wind nor yv is
+		// higher than reverse gravity
+		if (std::abs(xv) > std::abs(curWind))
+			xv = curWind;
+		if (yv < maxGravAccel)
+			yv = maxGravAccel;
+
+		// Don't push through the floor
+		if ( (y + yv) >= env.screenHeight) {
+			yv *= -0.5;
+			xv *=  0.95;
+		}
+
+		// The faster the smoke is blown by the wind, the less it rises:
+		if ( (yv < -1.) && (std::abs(xv) > 1.) )
+			yv = (yv + (yv / std::abs(xv))) / 2.;
+		// and if the smoke is going down, halve yv
+		else if (yv > 0.)
+			yv /= 2.;
+
+		// Now the velocity can be applied.
+		x += xv;
+		y += yv;
+
+		// Destroy the smoke if it goes off-screen or is diffused
+		int32_t calcRadius = static_cast<int32_t>(radius * (4.0 * age / maxAge));
+
+		if ( (x <  (1 - calcRadius))
+		  || (x >= (env.screenWidth + calcRadius))
+		  || (y <  (MENUHEIGHT - calcRadius))
+		  || (age > maxAge) )
+			destroy = true;
+	}
+}
+
+
+/// @brief draw decor according to current settings and type.
+void DECOR::draw()
+{
+	if (!ready && !destroy) {
+		updateDirt();
+		if (ready) {
+			// finished! See whether there are enough pixels
+			if (gotPixels <= radius)
+				// nope.
+				destroy = true;
+		}
+	}
+
+	if (destroy || (delay > 0))
+		return;
+
+	int32_t calcRadius = radius;
+
+	if (DECOR_DIRT == type) {
+		// Rotate according to xv and yv
+		angle += yv + ((SIGNd(xv) * 5.) - xv);
+
+		// Be sure the angle is in order:
+		if (angle <   0) angle += 360;
+		if (angle > 360) angle -= 360;
+
+		// And draw it:
+		if (y > MENUHEIGHT) {
+			VIRTUAL_OBJECT::draw();
+			++calcRadius;
+		}
+	} else if (DECOR_SMOKE == type) {
+		// The older, the larger...
+		calcRadius = static_cast<int32_t>(radius * (4.0 * age / maxAge));
+
+		drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
+		set_trans_blender (0, 0, 0, 255 - (255 * age / maxAge));
+		circlefill (global.canvas, x, y, calcRadius, color);
+	}
+
+	drawing_mode (global.current_drawing_mode, NULL, 0, 0);
+
+	setUpdateArea ( x - calcRadius - 1, y - calcRadius - 1,
+					(calcRadius * 2) + 2, (calcRadius * 2) + 2);
+	requireUpdate ();
+}
+
+
+/// In case of too much decor for the machine, allow forced ageing
+void DECOR::force_aging(int32_t frames)
+{
+	age += frames;
+	if (age > maxAge)
+		destroy = true;
+}
+
+
+/// return true if a dirt debris item "lies" on the floor, or is squeezed in a
+/// dirt slide.
+bool DECOR::isOnFloor()
+{
+	int32_t scr_r  = env.screenWidth - 2;  // shortcut;
+	int32_t scr_b  = env.screenHeight - 2; // ditto;
+
+	// If the debris is above the screen or directly on the floor,
+	// return at once:
+	if (y <= MENUHEIGHT)
+		return false;
+	if (y >= (scr_b - radius))
+		return true;
+
+	// Use safe values:
+	int32_t round_x = ROUND(x);
+	int32_t round_y = ROUND(y);
+
+	// sanitize x value:
+	if      (round_x < 1)     round_x = 1;
+	else if (round_x > scr_r) round_x = scr_r;
+
+	// rounded boundaries, clipped to the screen:
+	int32_t left    = std::max(1,          round_x - radius);
+	int32_t top     = std::max(MENUHEIGHT, round_y - radius);
+	int32_t right   = std::min(scr_r,      round_x + radius);
+	int32_t bottom  = std::min(scr_b,      round_y + radius);
+
+	// Go from left to right and check whether the surface is above the bottom.
+	int32_t surf_hits = 0;
+	bool    on_floor  = false;
+	for (int32_t i = left; !on_floor && (i <= right); ++i) {
+		if (global.surface[i].load(ATOMIC_READ) <= bottom) {
+			// Actually this could mean that the debris is within
+			// a hole in a mountain that hasn't been slid down, yet.
+			bool in_dirt = false;
+
+			for (int32_t j = bottom; !in_dirt && (j >= top) ; --j) {
+				if (PINK != getpixel(global.terrain, i, j))
+					in_dirt = true;
+			}
+
+			if (in_dirt && (++surf_hits >= radius) )
+				on_floor = true;
+		}
+	}
+
+	return on_floor;
+}
+
+
+/// DIRT and Smoke (somewhat) can be repulsed, too
+void DECOR::repulseDecor()
+{
+	TANK*  lt     = nullptr;
+	double xaccel = 0;
+	double yaccel = 0;
+
+	global.getHeadOfClass(CLASS_TANK, &lt);
+
+	while (lt) {
+		if (!lt->destroy) {
+
+			if (lt->repulse (x + xv, y + yv, &xaccel, &yaccel, physType)) {
+				xv += xaccel;
+				yv += yaccel;
+			}
+		}
+		lt->getNext(&lt);
+	}
+}
+
+
+/// Small scale dirt grabber
+void DECOR::updateDirt()
+{
+	int32_t togo    = grabPerCall + 1;
+	double  deb_rad = static_cast<double>(radius);
+
+	while (togo) {
+		double deb_dist = FABSDISTANCE2(static_cast<double>(grab_x),
+										static_cast<double>(grab_y),
+										deb_rad,
+										deb_rad);
+		if (deb_dist <= deb_rad) {
+			int32_t tcol = getpixel(dirt->bmp, grab_x, grab_y);
+
+			// If this is a meteor and the terrain had no pixel
+			// there, take one out of the rock instead.
+			if ( (PINK == tcol) && meteor)
+				tcol = getpixel(meteor->bmp, grab_x, grab_y);
+
+			// If this is valid, scorch the colour and put it back.
+			if (PINK != tcol) {
+				double deb_mod = deb_dist / deb_rad;
+				int32_t new_r = getr(tcol) / (1.25 + deb_mod);
+				int32_t new_g = getg(tcol) / (1.66 + deb_mod);
+				int32_t new_b = getb(tcol) / (1.33 + deb_mod);
+				putpixel(dirt->bmp, grab_x, grab_y, makecol(new_r, new_g, new_b));
+				++gotPixels;
+			}
+		} // End of position in range
+
+		else
+			// If the position is not in range, erase the surplus pixel
+			putpixel(dirt->bmp, grab_x, grab_y, PINK);
+
+		// This point is done
+		--togo;
+
+		// Advance coordinates
+		if (++grab_x > diameter) {
+			grab_x = 0;
+			if (++grab_y > diameter) {
+				// end of work
+				togo  = 0;
+				ready = true;
+				if (meteor) {
+					global.free_debris_item(meteor);
+					meteor = nullptr;
+				}
+			}
+		}
+	} // End of having pixels to grab
+}
diff --git a/src/decor.h b/src/decor.h
index ff29a2f..eaaa83a 100644
--- a/src/decor.h
+++ b/src/decor.h
@@ -21,149 +21,88 @@
  * */
 
 
-#include "main.h"
 #include "physobj.h"
-#include "environment.h"
-#include "globaldata.h"
+#include "debris_pool.h"
 
-enum	decorTypes
+enum decorTypes
 {
-  DECOR_SMOKE
+  DECOR_SMOKE = 0,
+  DECOR_DIRT
 };
 
 class DECOR: public PHYSICAL_OBJECT
-  {
-  public:
-    int	type;
-    int	radius;
-    int	color;
-
-    /* --- constructor --- */
-    inline DECOR (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, double xvel, double yvel,
-                  int maxRadius, int decorType):PHYSICAL_OBJECT()
-    {
-      type = decorType;
-      initialise ();
-      setEnvironment (env);
-      player = NULL;
-      _align = LEFT;
-      _global = global;
-      x = xpos;
-      y = ypos;
-      xv = xvel;
-      yv = yvel;
-      if (maxRadius <= 3)
-        radius = 3;
-      else
-        radius = 3 + (rand () % (maxRadius - 3));
-    }
-
-    /* --- destructor --- */
-    inline virtual ~DECOR ()
-    {
-      _env->removeObject (this);
-      _global = NULL;
-      _env    = NULL;
-    }
-
-    /* --- non-inline methods --- */
-
-    /* --- inline methods --- */
-
-    inline virtual void	draw (BITMAP *dest)
-    {
-      if (!destroy)
-        {
-          int iCalcRadius = radius;
-          if (type == DECOR_SMOKE) iCalcRadius = (int)(iCalcRadius * (4.0 * age / maxAge)); // The older, the larger...
-          drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-          set_trans_blender (0, 0, 0, 255 - (255 * age / maxAge));
-          circlefill (dest, (int)x, (int)y, iCalcRadius, color);
-          drawing_mode (DRAW_MODE_SOLID, NULL, 0, 0);
-          setUpdateArea ( (int)x - (iCalcRadius * 2) - 1,
-                          (int)y - (iCalcRadius * 2) - 1,
-                          iCalcRadius * 4 + 2,
-                          iCalcRadius * 4 + 2);
-          requireUpdate ();
-        }
-    }
-
-    inline virtual int	applyPhysics ()
-    {
-      /** Update: Smoke will age faster, if the wind is stronger! **/
-      int iAgeMod = 1;
-      if (_env->wind && (type == DECOR_SMOKE))
-        iAgeMod = (int)round(fabs(_env->wind / (_env->windstrength / 2.0))) + 1;
-      /* This produces: (with max wind = 8)
-       * wind = 1 : round(1 / (8 / 2)) + 1 = round(1 / 4) + 1 = 0 + 1 = 1 <-- normal aging
-       * wind = 4 : round(4 / (8 / 2)) + 1 = round(4 / 4) + 1 = 1 + 1 = 2 <-- raised aging
-       * wind = 6 : round(6 / (8 / 2)) + 1 = round(6 / 4) + 1 = 2 + 1 = 3 <-- fast aging
-       * wind = 8 : round(8 / (8 / 2)) + 1 = round(8 / 4) + 1 = 2 + 1 = 3 <-- fast aging */
-      age += iAgeMod;
-
-      if (!physics)
-        {
-          double xaccel = (_env->wind - xv) / mass * drag * _env->viscosity;
-          xv += xaccel;
-          x += xv;
-          if (x < 1 || x > (_global->screenWidth-1))
-            {
-              destroy = TRUE;
-            }
-          double yaccel = (-1 - yv) / mass * drag * _env->viscosity;
-          yv += yaccel;
-          if (y + yv >= _global->screenHeight)
-            {
-              yv = -yv * 0.5;
-              xv *= 0.95;
-            }
-          y += yv;
-        }
-      if (age > maxAge)
-        {
-          destroy = TRUE;
-        }
-
-      return (hitSomething);
-    }
-
-    inline virtual void	initialise ()
-    {
-      PHYSICAL_OBJECT::initialise ();
-      physics = 0;
-      age = 0;
-      if (type == DECOR_SMOKE)
-        {
-          int tempCol = 128 + (rand () % 64);
-          color = makecol (tempCol, tempCol, tempCol);
-          mass = 1 + ((double)(rand () % 5) / 10);
-          drag = 0.9 + ((double)(rand () % 90) / 100);
-          maxAge = 20 + (rand () % 90);
-        }
-    }
-
-    inline virtual int	isSubClass (int classNum)
-    {
-      if (classNum == DECOR_CLASS)
-        return (TRUE);
-      else
-        return (FALSE);
-    }
-
-    inline virtual int	getClass ()
-    {
-      return (DECOR_CLASS);
-    }
-
-    inline virtual void setEnvironment(ENVIRONMENT *env)
-    {
-      if (!_env || (_env != env))
-        {
-          _env = env;
-          _index = _env->addObject (this);
-        }
-    }
-
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	// ctor without bitmap
+	explicit
+	DECOR (double x_, double y_, double xv_, double yv_,
+	       int32_t maxRadius, int32_t type_, int32_t delay_);
+
+	// ctor with bitmap
+	DECOR (double x_, double y_, double xv_, double yv_,
+	       int32_t maxRadius, int32_t type_, int32_t delay_,
+	       sDebrisItem* deb_item, sDebrisItem* met_item);
+
+
+	~DECOR();
+
+
+	/* -----------------------------------
+	 * --- Public methods              ---
+	 * -----------------------------------
+	 */
+
+	void     applyPhysics ();
+	void     draw         ();
+	void     force_aging  (int32_t frames); // Helper to work against FPS drops.
+	eClasses getClass     () { return (DECOR_SMOKE == type
+	                           ? CLASS_DECOR_SMOKE
+	                           : CLASS_DECOR_DIRT); }
+	bool     isSmoke      () { return DECOR_SMOKE == type; }
+
+
+private:
+
+	typedef sDebrisItem item_t;
+
+
+	/* -----------------------------------
+	 * --- Private methods             ---
+	 * -----------------------------------
+	 */
+
+	bool isOnFloor   ();
+	void repulseDecor();
+	void updateDirt  ();
+
+
+	/* -----------------------------------
+	 * --- Private members             ---
+	 * -----------------------------------
+	 */
+
+	int32_t color        = BLACK;
+	double  curWind      = 0.;      //!< shortcut to help physics calculations.
+	int32_t delay        = -1;      //!< How long until debris must be on its way.
+	int32_t diameter     = 10;      //!< Pre-calculated shortcut for debris items.
+	item_t* dirt         = nullptr; //!< The debris item to throw around if not smoke.
+	int32_t gotPixels    = 0;       //!< Helper for phased debris creation.
+	int32_t grab_x       = 0;       //!< Helper for phased debris creation.
+	int32_t grab_y       = 0;       //!< Helper for phased debris creation.
+	int32_t grabPerCall  = 0;       //!< Helper for phased debris creation.
+	double  maxGravAccel = 1.;      //!< Pre-calculated physics helper.
+	double  maxWind      = 8;       //!< env.windstrength cast to double.
+	double  maxWindAccel = 1.;      //!< Pre-calculated physics helper.
+	item_t* meteor       = nullptr; //!< Metor data if not enough dirt was found, but a meteor stroke.
+	int32_t radius       = 5;
+	bool    ready        = false;   //!< Whether a debris item is finished or not.
+	int32_t type         = DECOR_SMOKE;
+};
 
 #endif
diff --git a/src/environment.cpp b/src/environment.cpp
index 2a96f50..22a5ba9 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -18,1096 +18,1310 @@
  * */
 
 
+#include "main.h"
 #include "environment.h"
 #include "globaldata.h"
-#include "virtobj.h"
 #include "missile.h"
 #include "tank.h"
 #include "files.h"
+#include "sound.h"
+#include "player.h"
 
-ENVIRONMENT::ENVIRONMENT (GLOBALDATA *global):_global(global),done(NULL),fp(NULL),h(NULL),surface(NULL),dropTo(NULL),
-    velocity(NULL),dropIncr(NULL),height(NULL),db(NULL),terrain(NULL),sky(NULL)
+#include <cassert>
+
+ENVIRONMENT::ENVIRONMENT ()
 {
-  gravity = 0.150;
-  viscosity = 0.5;
-  landSlideType = LANDSLIDE_GRAVITY;
-  landSlideDelay = MAX_GRAVITY_DELAY;
-  windstrength = 8;
-  windvariation = 1;
-  weapontechLevel = itemtechLevel = 5;
-  landType = LANDTYPE_RANDOM;
-  meteors = 0;
-  lightning = 0;
-  satellite = 0.0;
-  falling_dirt_balls = 0.0;
-  fog = 0;
-  wallType = 0.0;
-  dBoxedMode		=	0.0;	// default is 0 = off
-  dFadingText		=	0.0;	// defaults to 0 for backwards compatibilities sake
-  dShadowedText	=	0.0;	// defaults to 0 for backwards compatibilities sake
-  current_wallType = 0;
-  custom_background = 0.0;
-  bitmap_filenames = NULL;
-  number_of_bitmaps = 0;
-  current_drawing_mode = DRAW_MODE_SOLID;
-
-  done = new char[global->screenWidth];
-  fp = new int[global->screenWidth];
-  h = new int[global->screenWidth];
-  surface = new int[global->screenWidth];
-  dropTo = new int[global->screenWidth];
-  velocity = new double[global->screenWidth];
-  dropIncr = new double[global->screenWidth];
-  height = new double[global->screenWidth];
-
-  db = create_bitmap (global->screenWidth, global->screenHeight);
-  if (!db)
-    cout << "Failed to create db bitmap: " << allegro_error;
-  terrain = create_bitmap (global->screenWidth, global->screenHeight);
-  if (!terrain)
-    cout << "Failed to create terrain bitmap: " << allegro_error;
-  sky = create_bitmap (global->screenWidth, global->screenHeight - MENUHEIGHT);
-  if (!sky)
-    cout << "Failed to create sky bitmap: " << allegro_error;
-  waiting_sky = NULL;
-  waiting_terrain = NULL;
-  _global = global;
-  global_tank_index = 0;
-  volume_factor = MAX_VOLUME_FACTOR;
-
-  initialise ();
-  #ifdef THREADS
-  waiting_sky_lock = init_lock(waiting_sky_lock);
-  waiting_terrain_lock = init_lock(waiting_terrain_lock);
-  #endif
+	// Unfortunately Visual C++ can not initialize arrays using an initialization list
+	// although it is part of C++11. Gcc and clang do it fine btw...
+	memset(availableItems, 0, sizeof(int32_t) * THINGS);
+	memset(configDir,      0, sizeof(char)    * (PATH_MAX + 1));
+	memset(dataDir,        0, sizeof(char)    * (PATH_MAX + 1));
+	memset(game_name,      0, sizeof(char)    * GAMENAMELEN);
+	memset(playerOrder,    0, sizeof(PLAYER*) * MAXPLAYERS);
+	memset(server_name,    0, sizeof(char)    * 129);
+	memset(server_port,    0, sizeof(char)    * 129);
+	memset(slope,          0, sizeof(double)  * 720);
+
+
+
+	set_fps(60); // rock solid default.
+
+	fontHeight = 10; // Initial value
+
+	strncpy(server_name, "127.0.0.1", 127);
+	strncpy(server_port, "25645", 127);
+
+	// Reserve space for the players array:
+	// Note: The allPlayers array is dynamically (re-)allocated while loading
+	//       stored players from the configuration.
+	if ( (players = (PLAYER **) calloc( MAXPLAYERS, sizeof(PLAYER *) ) ) == nullptr)
+		perror ( "environment.cpp: Failed allocating memory for players");
+
+	// sin/cos short-cuts, With only 1° granularity the arrays are always
+	// faster than live calculations.
+	for (int32_t i = 0; i < 360; i++) {
+		slope[i][0] = std::sin(DEG2RAD(i));
+		slope[i][1] = std::cos(DEG2RAD(i));
+	}
 }
 
 
-
-/** @brief ~ENVIRONMENT default dtor
- *  *   *
- *   *     * Cleanly remove created objects
- *    *       */
+/** @brief default dtor
+ * Cleanly remove created objects
+ **/
 ENVIRONMENT::~ENVIRONMENT()
 {
-  int count;
-
-  #ifdef THREADS
-  destroy_lock(waiting_sky_lock);
-  destroy_lock(waiting_terrain_lock);
-  #endif
-
-  if (db)      destroy_bitmap(db);      db      = NULL;
-  if (sky)     destroy_bitmap(sky);     sky     = NULL;
-  if (waiting_sky) destroy_bitmap(sky); waiting_sky = NULL;
-  if (terrain) destroy_bitmap(terrain); terrain = NULL;
-
-  _global = NULL;
-
-  if (done)     delete [] (done);     done = NULL;
-  if (fp)       delete [] (fp);       fp = NULL;
-  if (h)        delete [] (h);        h = NULL;
-  if (surface)  delete [] (surface);  surface = NULL;
-  if (dropTo)   delete [] (dropTo);   dropTo = NULL;
-  if (velocity) delete [] (velocity); velocity = NULL;
-  if (dropIncr) delete [] (dropIncr); dropIncr = NULL;
-  if (height)   delete [] (height);   height = NULL;
-  if (bitmap_filenames)
-  {
-     for (count = 0; count < number_of_bitmaps; count++)
-     {
-        if (bitmap_filenames[count]) 
-           free(bitmap_filenames[count]);
-     }
-     free(bitmap_filenames);
-  }
-}
-#ifdef THREADS
-void ENVIRONMENT::destroy_lock(pthread_mutex_t* lock) {
-  if (lock)
-  {
-    int result = pthread_mutex_destroy(lock);
-    switch (result)
-    {
-      case 0:
-        //Successfully destroyed
-        break;
-      case EBUSY:
-        //Some thread forgot to unlock result
-        printf("%s:%i: Lock is still held.\n", __FILE__, __LINE__);
-        break;
-      case EINVAL:
-        //Invalid lock
-        printf("%s:%i: Lock is invalid.\n", __FILE__, __LINE__);
-        break;
-      default:
-        printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_destroy.\n", __FILE__, __LINE__, result);
-        break;
-    }
-    free(lock);
-  }
-}
-#endif
-#ifdef THREADS
-pthread_mutex_t* ENVIRONMENT::init_lock(pthread_mutex_t* lock) {
-  lock = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
-  if (!lock)
-  {
-    printf("%s:%i: Could not allocate memory.\n", __FILE__, __LINE__);
-  }
-  int result = pthread_mutex_init(lock, NULL);
-  switch (result)
-  {
-    case 0:
-      //Succesfully initialized
-      break;
-    case EAGAIN:
-      //Not enough resources
-      printf("%s:%i: Not enough resources to create mutex.\n", __FILE__, __LINE__);
-      break;
-    case ENOMEM:
-      printf("%s:%i: Not enough memory to create mutex.\n", __FILE__, __LINE__);
-      break;
-    case EPERM:
-      printf("%s:%i: Not authorized.\n", __FILE__, __LINE__);
-      break;
-    case EBUSY:
-      printf("%s:%i: The mutex is already initialized.\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      printf("%s:%i: Invalid attribute.\n", __FILE__, __LINE__);
-      break;
-    default:
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_init.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  return lock;
-}
-#endif
-#ifdef THREADS
-void ENVIRONMENT::lock(pthread_mutex_t* lock)
-{
-  int result = pthread_mutex_lock(lock);
-  switch (result)
-  {
-    case 0:
-      //Got the lock.
-      break;
-    case EINVAL:
-      //Priority too high
-      printf("%s:%i: Either this thread's priority is higher than the mutex's priority, or the mutex is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    case EAGAIN:
-      printf("%s:%i: Too many locks on the mutex.\n", __FILE__, __LINE__);
-      break;
-    case EDEADLK:
-      //We have the lock already
-      printf("%s:%i: Already have lock.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //What error is this?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_lock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-}
-#endif
-#ifdef THREADS
-void ENVIRONMENT::unlock(pthread_mutex_t* lock)
-{
-  int result = pthread_mutex_unlock(lock);
-  switch (result)
-  {
-    case 0:
-      //Released the lock
-      break;
-    case EPERM:
-      //Forgot to get a lock on the mutex
-      printf("%s:%i: Mutex isn't locked\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      //Uninitialized mutex
-      printf("%s:%i: waiting_sky_lock is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_unlock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-}
-#endif
-BITMAP* ENVIRONMENT::get_waiting_sky()
-{
-  #ifdef THREADS
-  lock(waiting_sky_lock);
-  #endif
-  BITMAP* w = waiting_sky;
-  #ifdef THREADS
-  unlock(waiting_sky_lock);
-  #endif
-  return w;
-}
-BITMAP* ENVIRONMENT::get_waiting_terrain()
-{
-  #ifdef THREADS
-  lock(waiting_terrain_lock);
-  #endif
-  BITMAP* w = waiting_terrain;
-  #ifdef THREADS
-  unlock(waiting_terrain_lock);
-  #endif
-  return w;
+	this->destroy();
 }
-/*
-This function saves the environment settings to a text
-file. Each line has the format
-name=value\n
 
-The function returns TRUE on success and FALSE on failure.
--- Jesse
-*/
-int ENVIRONMENT::saveToFile_Text (FILE *file)
+
+/// @brief add a player to the players[] array that will take part in the next game
+void ENVIRONMENT::addGamePlayer(PLAYER* player_)
 {
-  if (! file) return FALSE;
-
-  fprintf (file, "*ENV*\n");
-
-  fprintf (file, "WINDSTRENGTH=%f\n", windstrength);
-  fprintf (file, "WINDVARIATION=%f\n", windvariation);
-  fprintf (file, "VISCOSITY=%f\n", viscosity);
-  fprintf (file, "GRAVITY=%f\n", gravity);
-  fprintf (file, "WEAPONTECHLEVEL=%f\n", weapontechLevel);
-  fprintf (file, "ITEMTECHLEVEL=%f\n", itemtechLevel);
-  fprintf (file, "METEORS=%f\n", meteors);
-  fprintf (file, "LIGHTNING=%f\n", lightning);
-  fprintf (file, "SATELLITE=%f\n", satellite);
-  fprintf (file, "FOG=%f\n", fog);
-  fprintf (file, "LANDTYPE=%f\n", landType);
-  fprintf (file, "LANDSLIDETYPE=%f\n", landSlideType);
-  fprintf (file, "WALLTYPE=%f\n", wallType);
-  fprintf (file, "BOXMODE=%f\n", dBoxedMode);
-  fprintf (file, "TEXTFADE=%f\n", dFadingText);
-  fprintf (file, "TEXTSHADOW=%f\n", dShadowedText);
-  fprintf (file, "LANDSLIDEDELAY=%f\n", landSlideDelay);
-  fprintf (file, "FALLINGDIRTBALLS=%f\n", falling_dirt_balls);
-  fprintf (file, "CUSTOMBACKGROUND=%f\n", custom_background);
-  fprintf (file, "***\n");
-  return TRUE;
+	if (player_ && (numGamePlayers < MAXPLAYERS) ) {
+
+		// Ensure the player isn't already there:
+		for (int32_t i = 0; i < numGamePlayers; ++i) {
+			if (player_ == players[i])
+				return;
+		}
+
+		players[numGamePlayers++] = player_;
+
+		if (HUMAN_PLAYER == player_->type)
+			numHumanPlayers++;
+	}
 }
 
-/*
-This function loads environment settings from a text
-file. The function returns TRUE on success and FALSE if
-any erors are encountered.
--- Jesse
-*/
-int ENVIRONMENT::loadFromFile_Text (FILE *file)
+
+/// @brief create a new player or return nullptr if an error occurred
+PLAYER* ENVIRONMENT::createNewPlayer (const char* player_name)
 {
-  char line[MAX_CONFIG_LINE];
-  int equal_position, line_length;
-  char field[MAX_CONFIG_LINE], value[MAX_CONFIG_LINE];
-  char *result = NULL;
-  bool done = false;
-
-  // read until we hit line (char *)"*ENV*" or "***" or EOF
-  do
-    {
-      result = fgets(line, MAX_CONFIG_LINE, file);
-      if (! result)     // eof
-        return FALSE;
-      if (! strncmp(line, "***", 3) )     // end of record
-        return FALSE;
-    }
-  while ( strncmp(line, "*ENV*", 5) );     // read until we hit new record
+	PLAYER** reallocatedPlayers = nullptr;
+	PLAYER*  player             = nullptr;
 
-  while ( (result) && (!done) )
-    {
-      // read a line
-      memset(line, '\0', MAX_CONFIG_LINE);
-      result = fgets(line, MAX_CONFIG_LINE, file);
-      if (result)
-        {
-          // if we hit end of the record, stop
-          if (! strncmp(line, "***", 3) ) return TRUE;
-          // find equal sign
-          line_length = strlen(line);
-          // strip newline character
-          if ( line[line_length - 1] == '\n')
-            {
-              line[line_length - 1] = '\0';
-              line_length--;
-            }
-          equal_position = 1;
-          while ( ( equal_position < line_length) && (line[equal_position] != '=') )
-            equal_position++;
-          // make sure we have valid equal sign
-
-          if ( equal_position <= line_length )
-            {
-              // seperate field from value
-              memset(field, '\0', MAX_CONFIG_LINE);
-              memset(value, '\0', MAX_CONFIG_LINE);
-              strncpy(field, line, equal_position);
-              strcpy(value, & (line[equal_position + 1]));
-              // check for fields and values
-              if (! strcasecmp(field, "windstrength") )
-                sscanf(value, "%lf", &windstrength);
-              else if (! strcasecmp(field, "windvariation") )
-                sscanf(value, "%lf", &windvariation);
-              else if (! strcasecmp(field, "viscosity") )
-                sscanf(value, "%lf", &viscosity);
-              else if (! strcasecmp(field, "gravity") )
-                sscanf(value, "%lf", &gravity);
-              else if (! strcasecmp(field, "techlevel"))
-                {
-                  sscanf(value, "%lf", &weapontechLevel);
-                  itemtechLevel = weapontechLevel;    // for backward compatibility
-                }
-              else if (! strcasecmp(field, "weapontechlevel") )
-                sscanf(value, "%lf", &weapontechLevel);
-              else if (! strcasecmp(field, "itemtechlevel") )
-                sscanf(value, "%lf", &itemtechLevel);
-              else if (! strcasecmp(field, "meteors"))
-                sscanf(value, "%lf", &meteors);
-              else if (! strcasecmp(field, "lightning") )
-                sscanf(value, "%lf", &lightning);
-              else if (! strcasecmp(field, "satellite") )
-                sscanf(value, "%lf", &satellite);
-              else if (! strcasecmp(field, "fog") )
-                sscanf(value, "%lf", &fog);
-              else if (! strcasecmp(field, "landtype"))
-                sscanf(value, "%lf", &landType);
-              else if (! strcasecmp(field, "landslidetype"))
-                sscanf(value, "%lf", &landSlideType);
-              else if (! strcasecmp(field, "walltype"))
-                sscanf(value, "%lf", &wallType);
-              else if (! strcasecmp(field, "boxmode"))
-                sscanf(value, "%lf", &dBoxedMode);
-              else if (! strcasecmp(field, "textfade"))
-                sscanf(value, "%lf", &dFadingText);
-              else if (! strcasecmp(field, "textshadow"))
-                sscanf(value, "%lf", &dShadowedText);
-              else if (! strcasecmp(field, "landslidedelay"))
-                sscanf(value, "%lf", &landSlideDelay);
-              else if (! strcasecmp(field, "fallingdirtballs") )
-                sscanf(value, "%lf", &falling_dirt_balls);
-              else if (! strcasecmp(field, "custombackground") )
-                sscanf(value, "%lf", &custom_background);
-
-            }    // end of found field=value line
-
-        }     // end of read a line properly
-    }     // end of while not done
-
-  return TRUE;
-}
+	assert (player_name && "ERROR: player_name is nullptr!");
 
+	if (nullptr == player_name)
+		return nullptr;
 
+	if (getPlayerByName(player_name) > -1)
+		return nullptr;
 
-void ENVIRONMENT::initialise ()
-{
-  for (int count = 0; count < _global->screenWidth; count++)
-    {
-      h[count] = 0;
-      fp[count] = 0;
-      done[count] = 1;
-      dropTo[count] = _global->screenHeight - 1;
-      surface[count] = 0;
-    }
-  for (int count = 0; count < MAX_OBJECTS; count++)
-    objects[count] = NULL;
-  for (int count = 0; count < MAXPLAYERS; count++)
-    order[count] = NULL;
+	reallocatedPlayers = (PLAYER**)realloc (allPlayers,
+	                              sizeof (PLAYER*) * (numPermanentPlayers + 1));
 
-  clear_to_color (sky, PINK);
-  clear_to_color (db, WHITE);
-  clear_to_color (terrain, PINK);
+	if (reallocatedPlayers)
+		allPlayers = reallocatedPlayers;
+	else
+		perror ("environment.cpp: Failed allocating memory for reallocatedPlayers in ENVIRONMENT::createNewPlayer");
 
-  // oldFogX = 0;
-  // oldFogY = 0;
+	player = new PLAYER ();
+	if (!player)
+		perror ("environment.cpp: Failed allocating memory for player in ENVIRONMENT::createNewPlayer");
 
-  combineUpdates = TRUE;
+	player->index = numPermanentPlayers;
+	player->setName(player_name);
+	allPlayers[numPermanentPlayers++] = player;
+
+	return player;
 }
 
-int ENVIRONMENT::isItemAvailable (int itemNum)
+
+/// @brief This function gives credits, score and money to the winner(s).
+void ENVIRONMENT::creditWinners(int32_t winner)
 {
-  if (itemNum < WEAPONS)
-    {
-      if ((weapon[itemNum].warhead) ||
-          (weapon[itemNum].techLevel > weapontechLevel))
-        return (FALSE);
-    }
-  else if (item[itemNum - WEAPONS].techLevel > itemtechLevel)
-    {
-      return (FALSE);
-    }
-  return (TRUE);
+	if (winner == WINNER_DRAW)    // no winner
+		return;
+
+	int32_t team_members = 0;
+
+	if (winner == WINNER_JEDI) {
+		for (int32_t i = 0; i < numGamePlayers; ++i) {
+			if (TEAM_JEDI == players[i]->team) {
+				players[i]->score++;
+				players[i]->won++;
+				team_members++;
+			}
+		}
+	} else if (winner == WINNER_SITH) {
+		for (int32_t i = 0; i < numGamePlayers; ++i) {
+			if (TEAM_SITH == players[i]->team) {
+				players[i]->score++;
+				players[i]->won++;
+				team_members++;
+			}
+		}
+	} else if (winner < WINNER_NO_WIN) {
+		players[winner]->score++;
+		players[winner]->won++;
+		players[winner]->money += scoreRoundWinBonus;
+	}
+
+	// team gets their money too
+	if (team_members) {
+		int32_t team_bonus = scoreRoundWinBonus / team_members;
+		for (int32_t i = 0; i < numGamePlayers; ++i) {
+			if ( ((winner == WINNER_JEDI) && (players[i]->team == TEAM_JEDI) )
+			  || ((winner == WINNER_SITH) && (players[i]->team == TEAM_SITH) ) )
+				players[i]->money += team_bonus;
+		}
+	}
+}
+
+
+void ENVIRONMENT::decreaseVolume()
+{
+	if (volume_factor > 0)
+		--volume_factor;
 }
 
-void ENVIRONMENT::generateAvailableItemsList ()
+
+/// @brief Remove one of the players, then gone for good.
+void ENVIRONMENT::deletePermPlayer (PLAYER* player_)
 {
-  int slot = 0;
-  for (int itemNum = 0; itemNum < THINGS; itemNum++)
-    {
-      if (!isItemAvailable (itemNum))
-        continue;
-      availableItems[slot] = itemNum;
-      slot++;
-    }
-  numAvailable = slot;
+	int32_t toCount = 0;
+
+	for (int32_t fromCount = 0; fromCount < numPermanentPlayers; fromCount++) {
+		if (allPlayers[fromCount] != player_) {
+			if (allPlayers[toCount] != allPlayers[fromCount]) {
+				allPlayers[toCount]  = allPlayers[fromCount];
+				allPlayers[toCount]->index = toCount;
+			}
+			toCount++;
+		}
+	}
+	numPermanentPlayers--;
+
+	delete player_;
 }
 
-int ENVIRONMENT::addObject (VIRTUAL_OBJECT *object)
+
+/** @brief Free all allocated memory.
+  *
+  * Important: This MUST be called *before* allegro shuts down!
+**/
+void ENVIRONMENT::destroy()
 {
-  int objCount = 0;
+	if (sky) {
+		destroy_bitmap(sky);
+		sky = nullptr;
+	}
+
+	if (bitmap_filenames) {
+		for (int32_t count = 0; count < number_of_bitmaps; ++count) {
+			if (bitmap_filenames[count])
+				free(bitmap_filenames[count]);
+		}
+		free(bitmap_filenames);
+		bitmap_filenames = nullptr;
+	}
+
+	if (saved_game_list_size && saved_game_list) {
+		for (uint32_t i = 0; i < saved_game_list_size; ++i) {
+			if (saved_game_list[i])
+				free (const_cast<char*>(saved_game_list[i]));
+			saved_game_list[i] = nullptr;
+		}
+		free (saved_game_list);
+		saved_game_list = nullptr;
+		saved_game_list_size = 0;
+	}
+
+	if (music_dir) {
+		closedir(music_dir);
+		music_dir = nullptr;
+	}
+
+	if (background_music) {
+		destroy_sample(background_music);
+		background_music = nullptr;
+	}
+
+	if (sounds) {
+		int32_t index = 0;
+		while (sounds[index])
+			destroy_sample(sounds[index++]);
+		free(sounds);
+		sounds = nullptr;
+	}
+
+	if (title) {
+		int32_t index = 0;
+		while (title[index])
+			destroy_bitmap(title[index++]);
+		free(title);
+		title = nullptr;
+	}
+
+	if (button) {
+		int32_t index = 0;
+		while (button[index])
+			destroy_bitmap(button[index++]);
+		free(button);
+		button = nullptr;
+	}
+
+	if (misc) {
+		int32_t index = 0;
+		while (misc[index])
+			destroy_bitmap(misc[index++]);
+		free(misc);
+		misc = nullptr;
+	}
+
+	if (missile) {
+		int32_t index = 0;
+		while (missile[index])
+			destroy_bitmap(missile[index++]);
+		free(missile);
+		missile = nullptr;
+	}
+
+	if (stock) {
+		int32_t index = 0;
+		while (stock[index])
+			destroy_bitmap(stock[index++]);
+		free(stock);
+		stock = nullptr;
+	}
+
+	if (tank) {
+		int32_t index = 0;
+		while (tank[index])
+			destroy_bitmap(tank[index++]);
+		free(tank);
+		tank = nullptr;
+	}
+
+	if (tankgun) {
+		int32_t index = 0;
+		while (tankgun[index])
+			destroy_bitmap(tankgun[index++]);
+		free(tankgun);
+		tankgun = nullptr;
+	}
+
+	if (gloat)        { delete    gloat;        gloat        = nullptr; }
+	if (ingame)       { delete    ingame;       ingame       = nullptr; }
+	if (instructions) { delete    instructions; instructions = nullptr; }
+	if (panic)        { delete    panic;        panic        = nullptr; }
+	if (kamikaze)     { delete    kamikaze;     kamikaze     = nullptr; }
+	if (retaliation)  { delete    retaliation;  retaliation  = nullptr; }
+	if (revenge)      { delete    revenge;      revenge      = nullptr; }
+	if (suicide)      { delete    suicide;      suicide      = nullptr; }
+	if (war_quotes)   { delete    war_quotes;   war_quotes   = nullptr; }
+
+	if (allPlayers) {
+		for (int32_t i = 0; i < numPermanentPlayers; ++i) {
+			if (allPlayers[i])
+				delete allPlayers[i];
+			allPlayers[i]  = nullptr;
+		}
+		free(allPlayers);
+		allPlayers = nullptr;
+	}
+
+	if (players) {
+		for (int32_t i = 0; i < MAXPLAYERS; ++i)
+			players[i] = nullptr;
+		free(players);
+		players    = nullptr;
+	}
+
+	if (main_font) {
+		destroy_font(main_font);
+		main_font = nullptr;
+	}
+
+	gfxData.destroy();
+}
 
-  while ((objects[objCount] != NULL) && (objCount < MAX_OBJECTS))
-    objCount++;
-  if (objCount < MAX_OBJECTS)
-    objects[objCount] = object;
 
-  return (objCount);
+/// @brief Sets configDir to the path to the config directory used by atanks
+void ENVIRONMENT::find_config_dir()
+{
+	// If no config dir was given on the command line, try to find a valid one
+	if (!configDir[0]) {
+		// figure out file name
+		char* homedir = getenv(HOME_DIR);
+		snprintf(configDir, PATH_MAX, "%s/.atanks", homedir ? homedir : ".");
+
+		// copy the file over, if we did not yet
+		if (!Copy_Config_File()) {
+			// If it did not work, look whether the directory already exists:
+			DIR * pDestDir = opendir(env.configDir);
+			if (!pDestDir)
+				cerr << "ERROR: An error has occurred trying to set up"
+				     << " Atomic Tanks folders." << endl;
+			else {
+				closedir(pDestDir);
+				pDestDir = nullptr;
+			}
+		}
+	} // end of no config file on the command line
 }
 
-int ENVIRONMENT::removeObject (VIRTUAL_OBJECT *object)
+
+/// @brief Sets dataDir to the path 'unicode.dat' can be found in.
+bool ENVIRONMENT::find_data_dir()
 {
-  int objCount  = object->getIndex();
-  int objClass  = object->getClass();
 
-  object->requireUpdate();
-  object->update();
+	// If the datadir set by command line options, try that first
+	if (dataDir[0]) {
+		snprintf(path_buf, PATH_MAX, "%s/%s", DATA_DIR, "unicode.dat");
+		if (!access(path_buf, R_OK))
+			return true;
+		else {
+			cerr << "ERROR: The given datadir \"" << dataDir << "\""
+			     << " is invalid!" << endl;
+			memset(dataDir, 0, sizeof(char) * (PATH_MAX + 1));
+		}
+	}
+
+	// Try the set directory from the build
+	snprintf(path_buf, PATH_MAX, "%s/%s", DATA_DIR, "unicode.dat");
+	if (!access(path_buf, R_OK))
+		strncpy(dataDir, DATA_DIR, PATH_MAX);
+	else {
+        // This was not successful, try the current directory if not tried, yet.
+
+        if (strncmp(DATA_DIR, ".", 1) && strncmp(DATA_DIR, "./", 2)) {
+			strncpy(path_buf, "./unicode.dat", PATH_MAX);
+
+			// Try again and reset if unsuccessful
+			if (!access(path_buf, R_OK))
+				strncpy(dataDir, ".", PATH_MAX);
+        }
+	}
 
-  if (objCount < MAX_OBJECTS)
-    {
-      objects[objCount] = NULL;
-      if (objClass == TANK_CLASS)
-        for (int ordCount = 0; ordCount < MAXPLAYERS; ordCount++)
-          if (order[ordCount] == object)
-            order[ordCount] = NULL;
-    }
-  else
-    {
-      return (objCount);
-    }
+	// If dataDir is set, now, this was a success.
+	if (strlen(dataDir))
+		return true;
 
-  return (0);
+	return false;
 }
 
-VIRTUAL_OBJECT *ENVIRONMENT::getNextOfClass (int classNum, int *objCount)
+
+/// @brief Must be called before GLOBALDATA::first_init() is called!
+void ENVIRONMENT::first_init()
 {
-  for (;*objCount < MAX_OBJECTS; (*objCount)++)
-    {
-      if ((objects[*objCount] != NULL) &&
-          ((classNum == ANY_CLASS) || (classNum == objects[*objCount]->getClass ())))
-        break;
-    }
+	// Determine maximum number of updates before doing
+	// a full update:
+	max_screen_updates = ROUND(std::sqrt(ROUND(screenWidth / 8)
+	                                   * ROUND(screenHeight / 8)));
+	/* This is:
+	 *  800 x  600 => sqrt(100 *  75) => sqrt( 7500) =  87
+	 * 1280 x 1024 => sqrt(160 *  28) => sqrt(20480) = 143
+	 * 1600 x  900 => sqrt(200 * 113) => sqrt(22600) = 150
+	 * 1920 x 1080 => sqrt(240 * 135) => sqrt(32400) = 180
+	 */
+
+	// Get memory ...
+	if (!sky)
+		sky = create_bitmap (screenWidth, screenHeight - MENUHEIGHT);
+	if (!sky) {
+		cout << "Failed to create sky bitmap: " << allegro_error << endl;
+		exit(1);
+	}
+
+	initialise ();
+
+	menuBeginY = (screenHeight - 400) / 2;
+	if (menuBeginY < 0)
+		menuBeginY = 0;
+	menuEndY = screenHeight - menuBeginY;
+
+	gfxData.first_init();
+}
 
-  if (*objCount < MAX_OBJECTS)
-    return (objects[*objCount]);
-  else
-    return (NULL);
+
+/// @brief Fill availableItems array with everything buyable with current settings.
+void ENVIRONMENT::genItemsList ()
+{
+	int32_t slot = 0;
+	for (int32_t i = 0; i < THINGS; ++i) {
+		if (isItemAvailable(i))
+			availableItems[slot++] = i;
+	}
+	numAvailable = slot;
 }
 
-void ENVIRONMENT::newRound ()
+
+/// @brief return the index of the player with @a player_name or -1 if not found
+int32_t ENVIRONMENT::getPlayerByName(const char* player_name)
 {
-  for (int objCount = 0; objCount < MAX_OBJECTS; objCount++)
-    {
-      if (objects[objCount])
-        {
-          if ((!objects[objCount]->isSubClass (TANK_CLASS)) &&
-              (!objects[objCount]->isSubClass (FLOATTEXT_CLASS)))
-            {
-              delete objects[objCount];
-              objects[objCount] = NULL;
-            }
-          else if (objects[objCount]->isSubClass (TANK_CLASS))
-            ((TANK *)objects[objCount])->newRound();
-        }
-    }
+	int32_t result = -1;
 
-  naturals_since_last_shot = 0;
-  // set wall type
-  if (wallType == WALL_RANDOM)
-    current_wallType = rand() % 4;
-  else
-    current_wallType = (int) wallType;
+	assert (player_name && "ERROR: player_name is nullptr!");
 
-  // time_to_fall = (rand() % MAX_GRAVITY_DELAY) + 1;
-  time_to_fall = (rand() & (int)landSlideDelay) + 1;
-  global_tank_index = 0;
-}
+	if (nullptr == player_name)
+		return result;
 
-int ENVIRONMENT::ingamemenu ()
-{
-  int button[INGAMEBUTTONS];
-  int pressed = -1;
-  int updatew[INGAMEBUTTONS];
-  char *buttext[INGAMEBUTTONS];
-  buttext[0] = _global->ingame->complete_text[69];
-  buttext[1] =  _global->ingame->complete_text[70];
-  buttext[2] = _global->ingame->complete_text[71];
-  buttext[3] =  _global->ingame->complete_text[72];
-  // char buttext_de[INGAMEBUTTONS][32] = { "Zuruck", "Hauptmenu", "Verlassen", "Skip AI" };
-  int z, zz;
-
-  // Set/calcualte button size and positions
-  int button_width = 150;
-  int button_height = 20;
-  int button_space = 5;
-  int button_halfwidth = button_width / 2;
-  // int button_halfheight = button_height / 2;
-  int dialog_width = 200;
-  int dialog_height = ((INGAMEBUTTONS + 2) * button_height) + ((INGAMEBUTTONS + 1) * button_space);
-  int dialog_halfwidth = dialog_width / 2;
-  int dialog_halfheight = dialog_height / 2;
-
-  // Calculate button y values and set all button status to 0
-  int y = -dialog_halfheight + button_height + button_space;
-
-  for (z = 0; z < INGAMEBUTTONS; z++)
-  {
-    updatew[z] = 0;
-    button[z] = y;
-    y += button_height + button_space;
-  }
-
-  if (! _global->os_mouse) show_mouse (NULL);
-   make_update (_global->halfWidth - dialog_halfwidth, _global->halfHeight - dialog_halfheight, dialog_width, dialog_height);
-   rectfill (db, _global->halfWidth - dialog_halfwidth, _global->halfHeight - dialog_halfheight, _global->halfWidth + dialog_halfwidth - 1, _global->halfHeight + dialog_halfheight - 1, makecol (128, 128, 128));
-   rect (db, _global->halfWidth - dialog_halfwidth, _global->halfHeight - dialog_halfheight, _global->halfWidth + dialog_halfwidth - 1, _global->halfHeight + dialog_halfheight - 1, BLACK);
-
-
-  while (1)
-    {
-      LINUX_SLEEP;
-      if (keypressed ())
-        {
-          k = readkey ();
-          if ( (k >> 8 == KEY_ESC) || (k >> 8 == KEY_P) )
-            return (0);
-        }
+	for (int32_t i = 0; (-1 == result) && (i < numPermanentPlayers); ++i) {
+		if (!strcmp(player_name, allPlayers[i]->getName()))
+			result = i;
+	}
 
-      if (mouse_b & 1)
-        {
-          zz = 0;
-          for (z = 0; z < INGAMEBUTTONS; z++)
-            {
-                if (mouse_x >= _global->halfWidth - button_halfwidth && mouse_x < _global->halfWidth + button_halfwidth && mouse_y >= button[z] + _global->halfHeight
-                   && mouse_y < button[z] + button_height + _global->halfHeight)
-                {
-                  zz = 1;
-                  if (pressed > -1)
-                    updatew[pressed] = 1;
-                  pressed = z;
-                  updatew[z] = 1;
-                }
-            }
-          if (!zz)
-            {
-              if (pressed > -1)
-                updatew[pressed] = 1;
-              pressed = -1;
-            }
-        }
-      if (pressed > -1 && !mouse_b & 1)
-        return (pressed);
-      for (z = 0; z < INGAMEBUTTONS; z++)
-        {
-          if (updatew[z])
-            {
-              updatew[z] = 0;
-              make_update (_global->halfWidth - button_halfwidth, _global->halfHeight + button[z], button_width, button_height);
-            }
-        }
-      make_update (mouse_x, mouse_y, ((BITMAP *) (_global->misc[0]))->w, ((BITMAP *) (_global->misc[0]))->h);
-      make_update (lx, ly, ((BITMAP *) (_global->misc[0]))->w, ((BITMAP *) (_global->misc[0]))->h);
-      lx = mouse_x;
-      ly = mouse_y;
-      if (! _global->os_mouse)  show_mouse (NULL);
-      for (z = 0; z < INGAMEBUTTONS; z++)
-        {
-          draw_sprite (db, (BITMAP *) _global->misc[(pressed == z) ? 8 : 7], _global->halfWidth - button_halfwidth, _global->halfHeight + button[z]);
-          textout_centre_ex (db, font, buttext[z], _global->halfWidth, _global->halfHeight + button[z] + 6, WHITE, -1);
-        }
-      if (! _global->os_mouse) show_mouse (db);
-      do_updates ();
-    }
+	return result;
 }
 
-void ENVIRONMENT::do_updates ()
-{
-  int count;
 
-  bool bIsBgUpdNeeded = false;
-  if (_global->lastUpdatesCount)
-    bIsBgUpdNeeded = true;
+void ENVIRONMENT::increaseVolume()
+{
+	if (volume_factor < MAX_VOLUME_FACTOR)
+		++volume_factor;
+}
 
-  if (_global->updateCount >= MAXUPDATES)
-    make_fullUpdate();
-  else
-    {
-      for (count = 0; count < _global->updateCount && count < MAXUPDATES; count++)
-        {
-          blit (db, screen, _global->updates[count].x, _global->updates[count].y, _global->updates[count].x, _global->updates[count].y, _global->updates[count].w, _global->updates[count].h);
-          // Debug rectangle
-          //rect (screen, _global->updates[count].x, _global->updates[count].y, _global->updates[count].x + _global->updates[count].w, _global->updates[count].y + _global->updates[count].h, WHITE);
-          if (bIsBgUpdNeeded)
-            make_bgupdate(_global->updates[count].x, _global->updates[count].y, _global->updates[count].w, _global->updates[count].h);
-        }
 
-      if (!bIsBgUpdNeeded)
-        {
-          _global->lastUpdatesCount = _global->updateCount;
-          memcpy (_global->lastUpdates, _global->updates, sizeof (BOX) * _global->updateCount);
-        }
-      _global->updateCount = 0;
-    }
+int32_t ENVIRONMENT::ingamemenu ()
+{
+	int32_t     pressed   = -1;
+	bool        need_draw = true;
+	int32_t     button[INGAMEBUTTONS];
+	bool        updatew[INGAMEBUTTONS];
+	const char* buttext[INGAMEBUTTONS] = {
+		ingame->Get_Line(69),
+		ingame->Get_Line(70),
+		ingame->Get_Line(71),
+		ingame->Get_Line(72),
+	};
+
+	// Set/calculate button size and positions
+	int32_t b_width  = 150;
+	int32_t b_height = 20;
+	int32_t b_space  = 5;
+	int32_t b_half_w = b_width / 2;
+	int32_t b_left   = halfWidth - b_half_w;
+	int32_t b_right  = halfWidth + b_half_w - 1;
+
+	int32_t d_width  = 200;
+	int32_t d_height = ((INGAMEBUTTONS + 2) * b_height)
+	                 + ((INGAMEBUTTONS + 1) * b_space);
+	int32_t d_half_w = d_width / 2;
+	int32_t d_half_h = d_height / 2;
+
+	int32_t d_left   = halfWidth  - d_half_w;
+	int32_t d_right  = halfWidth  + d_half_w - 1;
+	int32_t d_top    = halfHeight - d_half_h;
+	int32_t d_bottom = halfHeight + d_half_h - 1;
+
+	// store last mouse coordinates for movement detection
+	int32_t lastMouse_x     = 0;
+	int32_t lastMouse_y     = 0;
+
+	// Calculate button y values and set all button status to 0
+	int32_t y = -d_half_h + b_height + b_space;
+
+	for (int32_t i = 0; i < INGAMEBUTTONS; ++i) {
+		updatew[i] = false;
+		button[i]  = y;
+		y += b_height + b_space;
+	}
+
+	SHOW_MOUSE(nullptr);
+	k = 0;
+	K = 0;
+
+	global.make_update (d_left, d_top, d_width, d_height);
+	rectfill (global.canvas, d_left, d_top, d_right, d_bottom, GREY);
+	rect     (global.canvas, d_left, d_top, d_right, d_bottom, BLACK);
+
+	while (-1 == pressed) {
+		LINUX_REST;
+		if (keypressed ()) {
+			k = readkey ();
+			K = k >> 8;
+		}
+
+		// look for keyboard exit
+		if ( ( K == KEY_ESC)
+		  || ( K == KEY_P) ) {
+			pressed = -2;
+			continue;
+		}
+
+		// Check mouse movement
+		if ( !env.osMouse
+		  && ( (lastMouse_x != mouse_x)
+		    || (lastMouse_y != mouse_y) ) ) {
+			lastMouse_x = mouse_x;
+			lastMouse_y = mouse_y;
+			need_draw = true;
+		}
+
+		if (mouse_b & 1) {
+			bool is_hit = false;
+			for (int32_t i = 0; !is_hit && (i < INGAMEBUTTONS); ++i) {
+				if ( (mouse_x >= b_left)
+				  && (mouse_x <  b_right)
+				  && (mouse_y >= (button[i] + halfHeight))
+				  && (mouse_y <  (button[i] + b_height + halfHeight)) ) {
+
+					is_hit = true;
+
+					if (pressed > -1)
+						updatew[pressed] = true;
+					pressed    = i;
+					updatew[i] = true;
+				}
+			}
+
+			if (!is_hit) {
+				if (pressed > -1)
+					updatew[pressed] = true;
+				pressed = -1;
+			}
+		}
+
+		// Update buttons
+		for (int32_t i = 0; i < INGAMEBUTTONS; ++i) {
+			if (updatew[i]) {
+				updatew[i] = false;
+				global.make_update (b_left, halfHeight + button[i],
+				                         b_width, b_height);
+			}
+		}
+
+		// Draw buttons
+		if (need_draw) {
+			SHOW_MOUSE(nullptr)
+
+			for (int32_t i = 0; i < INGAMEBUTTONS; ++i) {
+				draw_sprite (global.canvas, misc[(pressed == i) ? 8 : 7],
+							 b_left, halfHeight + button[i]);
+				textout_centre_ex (global.canvas, font, buttext[i], halfWidth,
+								   halfHeight + button[i] + 1, WHITE, -1);
+			}
+
+			// Update non-OS mouse movements
+			SHOW_MOUSE(global.canvas)
+
+			global.do_updates ();
+			need_draw = false;
+		}
+	} // end of menu loop
+
+	return pressed;
 }
 
-void ENVIRONMENT::replaceCanvas ()
+
+void ENVIRONMENT::initialise ()
 {
-  int count;
+	campaign_rounds = static_cast<double>(rounds) / 5.;
+	if (campaign_rounds < 1.)
+		campaign_rounds = 1.;
 
-  if (_global->lastUpdatesCount >= MAXUPDATES)
-    make_fullUpdate();
-  else
-    {
-      for (count = 0; count < _global->lastUpdatesCount && count < MAXUPDATES; count++)
-        {
-          blit (sky, db, _global->lastUpdates[count].x, _global->lastUpdates[count].y - MENUHEIGHT, _global->lastUpdates[count].x, _global->lastUpdates[count].y, _global->lastUpdates[count].w, _global->lastUpdates[count].h);
-          masked_blit (terrain, db, _global->lastUpdates[count].x, _global->lastUpdates[count].y, _global->lastUpdates[count].x, _global->lastUpdates[count].y, _global->lastUpdates[count].w, _global->lastUpdates[count].h);
-        }
-          #ifdef NEW_GAMELOOP
-          int iLeft = 0;
-          int iRight = _global->screenWidth - 1;
-          int iTop = MENUHEIGHT;
-          int iBottom = _global->screenHeight - 1;
-
-          vline(db, iLeft, iTop, iBottom, wallColour);    // Left edge
-          vline(db, iLeft + 1, iTop, iBottom, wallColour);    // Left edge
-          vline(db, iRight, iTop, iBottom, wallColour);   // right edge
-          vline(db, iRight - 1, iTop, iBottom, wallColour);   // right edge
-          hline(db, iLeft, iBottom, iRight, wallColour);// bottom edge
-          if (_global->bIsBoxed)
-              hline(db, iLeft, iTop, iRight, wallColour);// top edge
-          #endif
-      _global->lastUpdatesCount = 0;
-      // The menu needs to be redrawn:
-      make_update(0, 0, _global->screenWidth - 1, MENUHEIGHT);
-    }
+	nextCampaignRound = static_cast<double>(rounds) - campaign_rounds;
 }
 
-void ENVIRONMENT::make_update (int x, int y, int w, int h)
+
+/// @return true if the items tech level is not too high and if it is not a warhead.
+bool ENVIRONMENT::isItemAvailable (int32_t itemNum)
 {
-  int combined = 0;
-  if (combineUpdates && _global->updateCount && _global->updateCount < MAXUPDATES)
-    {
-      BOX prev, next;
-      prev.x = _global->updates[_global->updateCount - 1].x;
-      prev.y = _global->updates[_global->updateCount - 1].y;
-      // .w = .x + .w = x2 (aka renamed to be the right x-position)
-      prev.w = _global->updates[_global->updateCount - 1].x + _global->updates[_global->updateCount - 1].w;
-      // .h = .y + .h = y2 (aka renamed to be the bottom y-position)
-      prev.h = _global->updates[_global->updateCount - 1].y + _global->updates[_global->updateCount - 1].h;
-
-      next.x = x;
-      next.y = y;
-      next.w = x + w; // same as above, becoming x2 and not the width!
-      next.h = y + h; // same as above, becoming y2 and not the height!
-
-      if (((next.w > prev.x - 3) && (prev.w > next.x - 3)) &&
-          ((next.h > prev.y - 3) && (prev.h > next.y - 3)))
-        {
-          next.x = (next.x < prev.x)?next.x:prev.x;
-          next.y = (next.y < prev.y)?next.y:prev.y;
-          next.w = (next.w > prev.w)?next.w:prev.w;
-          next.h = (next.h > prev.h)?next.h:prev.h;
-          _global->updates[_global->updateCount - 1].x = next.x;
-          _global->updates[_global->updateCount - 1].y = next.y;
-          _global->updates[_global->updateCount - 1].w = next.w - next.x;
-          _global->updates[_global->updateCount - 1].h = next.h - next.y;
-          combined = 1;
-        }
-    }
-  if (!combined)
-    {
-      _global->updates[_global->updateCount].x = x;
-      _global->updates[_global->updateCount].y = y;
-      _global->updates[_global->updateCount].w = w;
-      _global->updates[_global->updateCount].h = h;
-      if (_global->updateCount < MAXUPDATES)
-        _global->updateCount++;
-    }
+	if (itemNum < WEAPONS) {
+		if ( (weapon[itemNum].warhead)
+		  || (weapon[itemNum].techLevel > weapontechLevel) )
+			return false;
+	} else if (item[itemNum - WEAPONS].techLevel > itemtechLevel)
+		return false;
+	return true;
+}
 
-  if (_global->updateCount >= MAXUPDATES)
-    make_fullUpdate();
-  else if (!_global->stopwindow)
-    {
-      if (x < _global->window.x)
-        _global->window.x = x;
-      if (y < _global->window.y)
-        _global->window.y = y;
-      if (x + w > _global->window.w)
-        _global->window.w = (x + w) - 1;
-      if (y + h > _global->window.h)
-        _global->window.h = (y + h) - 1;
-      if (_global->window.x < 0)
-        _global->window.x = 0;
-      if (_global->window.y < MENUHEIGHT)
-        _global->window.y = MENUHEIGHT;
-      if (_global->window.w > (_global->screenWidth-1))
-        _global->window.w = (_global->screenWidth-1);
-      if (_global->window.h > (_global->screenHeight-1))
-        _global->window.h = (_global->screenHeight-1);
-    }
+
+/*
+This function loads environment settings from a text
+file. The function returns TRUE on success and FALSE if
+any erors are encountered.
+-- Jesse
+*/
+/// @todo : This should be changed to streams. It's C++ and we have formatted
+/// text files, so formatted input/output should be far more efficient in
+/// maintenance.
+void ENVIRONMENT::load_from_file (FILE *file)
+{
+	char  line[MAX_CONFIG_LINE  + 1] = { 0 };
+	char  field[MAX_CONFIG_LINE + 1] = { 0 };
+	char  value[MAX_CONFIG_LINE + 1] = { 0 };
+	char* result                     = nullptr;
+	bool  done                       = false;
+	bool  sound_bookmark = sound_enabled; // To disable by command line
+
+	// read until we hit line "*ENV*" or "***" or EOF
+	do {
+		result = fgets(line, MAX_CONFIG_LINE, file);
+		if (!result || !strncmp(line, "***", 3) )
+			// eof or end of record
+			return;
+		else if (!strncmp(line, "*GLOBAL*", 8)) {
+			// Old style config/save file
+			rewind(file);
+			global.load_from_file(file);
+		}
+    } while ( strncmp(line, "*ENV*", 5) );
+	// read until we hit new record
+
+	while ( (result) && (!done) ) {
+		// read a line
+		memset(line, 0, MAX_CONFIG_LINE);
+		result = fgets(line, MAX_CONFIG_LINE, file);
+
+		// if we hit end of the record, stop
+		if (! strncmp(line, "***", 3) )
+			done = true;
+
+		if (result && !done) {
+
+			// strip newline character
+			int32_t line_length = strlen(line);
+			while ( line[line_length - 1] == '\n') {
+				line[line_length - 1] = '\0';
+				line_length--;
+			}
+
+			// find equal sign
+			int32_t equal_position = 1;
+			while ( ( equal_position < line_length )
+				 && ( line[equal_position] != '='  ) )
+				equal_position++;
+
+			// make sure the equal sign position is valid
+			if (line[equal_position] != '=')
+				continue; // Go to next line
+
+			// seperate field from value
+			memset(field, '\0', MAX_CONFIG_LINE);
+			memset(value, '\0', MAX_CONFIG_LINE);
+			strncpy(field, line, equal_position);
+			strncpy(value, & (line[equal_position + 1]), 127);
+
+			// check for fields and values
+			if (!strcasecmp(field, "acceleratedai")) {
+				sscanf(value, "%d", &skipComputerPlay);
+				if (skipComputerPlay > SKIP_HUMANS_DEAD)
+					skipComputerPlay = SKIP_HUMANS_DEAD;
+			} else if (!strcasecmp(field, "checkupdates")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				check_for_updates = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "colourtheme") ) {
+				sscanf(value, "%d", &colourTheme);
+				if (colourTheme < CT_REGULAR) colourTheme = CT_REGULAR;
+				if (colourTheme > CT_CRISPY)  colourTheme = CT_CRISPY;
+			} else if (!strcasecmp(field, "debrislevel") )
+				sscanf(value, "%d", &debris_level);
+			else if (!strcasecmp(field, "detailedland")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				detailedLandscape = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "detailedsky")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				detailedSky = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "dither")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				ditherGradients = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "dividemoney") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				divide_money = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "doboxwrap") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				do_box_wrap = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "dynamicmenubg") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				dynamicMenuBg = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "frames") ) {
+				int32_t new_fps = 0;
+				sscanf(value, "%d", &new_fps);
+				set_fps(new_fps);
+			} else if (!strcasecmp(field, "fullscreen"))
+				sscanf(value, "%d", &full_screen);
+			else if (!strcasecmp(field, "interest"))
+				sscanf(value, "%lf", &interest);
+			else if (!strcasecmp(field, "language") ) {
+				uint32_t stored_lang = 0;
+				sscanf(value, "%u", &stored_lang);
+				language = static_cast<eLanguages>(stored_lang);
+			} else if (!strcasecmp(field, "maxfiretime") )
+				sscanf(value, "%d", &maxFireTime);
+			else if (!strcasecmp(field, "networking")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				network_enabled = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "networkport"))
+				sscanf(value, "%d", &network_port);
+			else if (!strcasecmp(field, "numpermanentplayers"))
+				sscanf(value, "%d", &numPermanentPlayers);
+			else if (!strcasecmp(field, "osmouse")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				osMouse = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "playmusic")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				play_music = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "rounds") )
+				sscanf(value, "%u", &rounds);
+			else if (!strcasecmp(field, "scorehitunit"))
+				sscanf(value, "%d", &scoreHitUnit);
+			else if (!strcasecmp(field, "scoreroundwinbonus"))
+				sscanf(value, "%d", &scoreRoundWinBonus);
+			else if (!strcasecmp(field, "scoreselfhit"))
+				sscanf(value, "%d", &scoreSelfHit);
+			else if (!strcasecmp(field, "scoreteamhit"))
+				sscanf(value, "%d", &scoreTeamHit);
+			else if (!strcasecmp(field, "scoreunitdestroybonus"))
+				sscanf(value, "%d", &scoreUnitDestroyBonus);
+			else if (!strcasecmp(field, "scoreunitselfdestroy"))
+				sscanf(value, "%d", &scoreUnitSelfDestroy);
+			else if (!strcasecmp(field, "sellpercent"))
+				sscanf(value, "%lf", &sellpercent);
+#ifdef NETWORK
+			else if ( !strcasecmp(field, "servername") )
+				sscanf(value, "%*[']%[^']%*[']", server_name);
+			else if ( !strcasecmp(field, "serverport") )
+				sscanf(value, "%*[']%[^']%*[']", server_port);
+#endif // NETWORK
+			else if ( !strcasecmp(field, "showaifeedback") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				showAIFeedback = val > 0 ? true : false;
+			} else if ( !strcasecmp(field, "showfps") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				showFPS = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "soundenabled")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				sound_enabled = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "sounddriver"))
+				sscanf(value, "%d", &sound_driver);
+			else if (!strcasecmp(field, "startmoney"))
+				sscanf(value, "%d", &startmoney);
+			else if (!strcasecmp(field, "turntype"))
+				sscanf(value, "%d", &turntype);
+			else if (!strcasecmp(field, "violentdeath") )
+				sscanf(value, "%d", &violent_death);
+			else if (!strcasecmp(field, "windstrength") )
+				sscanf(value, "%d", &windstrength);
+			else if (!strcasecmp(field, "windvariation") )
+				sscanf(value, "%d", &windvariation);
+			else if (!strcasecmp(field, "viscosity") ) {
+				sscanf(value, "%lf", &viscosity);
+			if (viscosity < 0.25)
+				viscosity = 0.5;
+			} else if (!strcasecmp(field, "gravity") ) {
+				sscanf(value, "%lf", &gravity);
+				if (gravity < 0.025)
+					gravity = 0.15;
+			} else if (!strcasecmp(field, "techlevel")) {
+				sscanf(value, "%d", &weapontechLevel);
+				itemtechLevel = weapontechLevel;    // for backward compatibility
+			} else if (!strcasecmp(field, "weapontechlevel") )
+				sscanf(value, "%d", &weapontechLevel);
+			else if (!strcasecmp(field, "itemtechlevel") )
+				sscanf(value, "%d", &itemtechLevel);
+			else if (!strcasecmp(field, "meteors"))
+				sscanf(value, "%d", &meteors);
+			else if (!strcasecmp(field, "lightning") )
+				sscanf(value, "%d", &lightning);
+			else if (!strcasecmp(field, "satellite") )
+				sscanf(value, "%d", &satellite);
+			else if (!strcasecmp(field, "fog") )
+				sscanf(value, "%d", &fog);
+			else if (!strcasecmp(field, "landtype"))
+				sscanf(value, "%d", &landType);
+			else if (!strcasecmp(field, "landslidetype"))
+				sscanf(value, "%d", &landSlideType);
+			else if (!strcasecmp(field, "walltype"))
+				sscanf(value, "%d", &wallType);
+			else if (!strcasecmp(field, "boxmode"))
+				sscanf(value, "%d", &boxedMode);
+			else if (!strcasecmp(field, "textfade")) {
+				int32_t res = 0;
+				sscanf(value, "%d", &res);
+				fadingText = res ? true : false;
+			} else if (!strcasecmp(field, "textshadow")) {
+				int32_t res = 0;
+				sscanf(value, "%d", &res);
+				shadowedText = res ? true : false;
+			} else if (!strcasecmp(field, "textsway")) {
+				int32_t res = 0;
+				sscanf(value, "%d", &res);
+				swayingText = res ? true : false;
+			} else if (!strcasecmp(field, "landslidedelay"))
+				sscanf(value, "%d", &landSlideDelay);
+			else if (!strcasecmp(field, "fallingdirtballs") ) {
+				sscanf(value, "%d", &falling_dirt_balls);
+				if (falling_dirt_balls < 0) falling_dirt_balls = 0;
+				if (falling_dirt_balls > 3) falling_dirt_balls = 3;
+			} else if (!strcasecmp(field, "custombackground") )
+				sscanf(value, "%d", &custom_background);
+			else if (!strcasecmp(field, "volumefactor") )
+				sscanf(value, "%d", &volume_factor);
+			else if (!strcasecmp(field, "volleydelay") )
+				sscanf(value, "%d", &volley_delay);
+			else if (!strcasecmp(field, "screenwidth"))
+				sscanf(value, "%d", &screenWidth);
+			else if (!strcasecmp(field, "screenheight"))
+				sscanf(value, "%d", &screenHeight);
+		}     // end of read a line properly
+	}     // end of while not done
+
+	// If values were set on the command line, override
+	// configuration values:
+	if (temp_screenHeight)
+		screenHeight = temp_screenHeight;
+	else
+		temp_screenHeight = screenHeight;
+	if (temp_screenWidth)
+		screenWidth = temp_screenWidth;
+	else
+		temp_screenWidth = screenWidth;
+
+	// The resolution must not be below 800x600:
+	if (screenHeight < 600)
+		screenHeight = 600;
+	if (screenWidth  < 800)
+		screenWidth  = 800;
+
+	// The screen resolution values must be copied back into
+	// the temp variables, which are then used by the menu,
+	// or changing the resolution will make the menu exit crash.
+	// The resolution is set only once on game start, so
+	// these changes go into temp and are stored back from them.
+	temp_screenHeight = screenHeight;
+	temp_screenWidth  = screenWidth;
+
+	if (! sound_bookmark)
+		sound_enabled = false;
+
+	halfWidth = screenWidth / 2;
+	halfHeight = screenHeight / 2;
+
+	menuBeginY = (screenHeight - 400) / 2;
+	if (menuBeginY < 0) menuBeginY = 0;
+	menuEndY = screenHeight - menuBeginY;
+
+	return;
 }
 
-void ENVIRONMENT::make_bgupdate (int x, int y, int w, int h)
+
+#define LOAD_TEXT_BLOCK(var, file) try { \
+	snprintf(path_buf, PATH_MAX, "%s/text/%s%s", dataDir, file, suffix); \
+	TEXTBLOCK* new_##var = new TEXTBLOCK(path_buf); \
+	if (var) \
+		delete var; \
+	var = new_##var; \
+} catch (...) { }
+
+/** @brief load text files according to set language
+  * This function loads all needed text files, based on
+  * language, into memory. If a previous text was loaded, it is
+  * removed from memory first.
+**/
+void ENVIRONMENT::load_text_files()
 {
-  int combined = 0;
-  if (_global && combineUpdates && _global->lastUpdatesCount && (_global->lastUpdatesCount < MAXUPDATES))
-    {
-      BOX prev, next;
-      prev.x = _global->lastUpdates[_global->lastUpdatesCount - 1].x;
-      prev.y = _global->lastUpdates[_global->lastUpdatesCount - 1].y;
-      prev.w = _global->lastUpdates[_global->lastUpdatesCount - 1].x + _global->lastUpdates[_global->lastUpdatesCount - 1].w;
-      prev.h = _global->lastUpdates[_global->lastUpdatesCount - 1].y + _global->lastUpdates[_global->lastUpdatesCount - 1].h;
-
-      next.x = x;
-      next.y = y;
-      next.w = x + w;
-      next.h = y + h;
-
-      if (((next.w > prev.x - 3) && (prev.w > next.x - 3)) &&
-          ((next.h > prev.y - 3) && (prev.h > next.y - 3)))
-        {
-          next.x = (next.x < prev.x)?next.x:prev.x;
-          next.y = (next.y < prev.y)?next.y:prev.y;
-          next.w = (next.w > prev.w)?next.w:prev.w;
-          next.h = (next.h > prev.h)?next.h:prev.h;
-          _global->lastUpdates[_global->lastUpdatesCount - 1].x = next.x;
-          _global->lastUpdates[_global->lastUpdatesCount - 1].y = next.y;
-          _global->lastUpdates[_global->lastUpdatesCount - 1].w = next.w - next.x;
-          _global->lastUpdates[_global->lastUpdatesCount - 1].h = next.h - next.y;
-          combined = 1;
-        }
-    }
-  if (_global && !combined)
-    {
-      _global->lastUpdates[_global->lastUpdatesCount].x = x;
-      _global->lastUpdates[_global->lastUpdatesCount].y = y;
-      _global->lastUpdates[_global->lastUpdatesCount].w = w;
-      _global->lastUpdates[_global->lastUpdatesCount].h = h;
-      if (_global->lastUpdatesCount < MAXUPDATES)
-        _global->lastUpdatesCount++;
-    }
+	char suffix[12] = { 0 };
+
+	switch (language) {
+		case EL_FRENCH:
+			strncpy(suffix, "_fr.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes.txt", dataDir);
+			break;
+		case EL_GERMAN:
+			strncpy(suffix, "_de.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes.txt", dataDir);
+			break;
+		case EL_ITALIAN:
+			strncpy(suffix, "_it.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes_it.txt", dataDir);
+			break;
+		case EL_PORTUGUESE:
+			strncpy(suffix, ".pt_BR.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes.txt", dataDir);
+			break;
+		case EL_RUSSIAN:
+			strncpy(suffix, "_ru.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes_ru.txt", dataDir);
+			break;
+		case EL_SLOVAK:
+			strncpy(suffix, "_sk.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes.txt", dataDir);
+			break;
+		case EL_SPANISH:
+			strncpy(suffix, "_ES.txt", 11);
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes_ES.txt", dataDir);
+			break;
+		case EL_ENGLISH:
+		default:
+			strncpy(suffix, ".txt", 11);       // default to english
+			snprintf(path_buf, PATH_MAX, "%s/text/war_quotes.txt", dataDir);
+			break;
+	}
+
+	try {
+		TEXTBLOCK* new_war_quotes = new TEXTBLOCK(path_buf);
+		if (war_quotes)
+			delete war_quotes;
+		war_quotes = new_war_quotes;
+	} catch (...) { /* can't do anything helpful here anyway */ }
+
+
+	LOAD_TEXT_BLOCK(gloat, "gloat")
+	LOAD_TEXT_BLOCK(ingame, "ingame")
+	LOAD_TEXT_BLOCK(instructions, "instr")
+	LOAD_TEXT_BLOCK(panic, "panic")
+	LOAD_TEXT_BLOCK(kamikaze, "kamikaze")
+	LOAD_TEXT_BLOCK(retaliation, "retaliation")
+	LOAD_TEXT_BLOCK(revenge, "revenge")
+	LOAD_TEXT_BLOCK(suicide, "suicide")
+}
+
 
-  if (_global && _global->lastUpdatesCount >= MAXUPDATES)
-    make_fullUpdate();
+/** @brief load a background music file.
+  *
+  * This function loads a music file (if there is one available.)
+  * If a current sample is set, it will be released.
+  *
+  * @return true if a sample is loaded, false otherwise.
+**/
+bool ENVIRONMENT::loadBackgroundMusic()
+{
+	static bool isSecondTry = false;
+
+	// see if we should bother
+	if (!play_music)
+		return false;
+
+	SAMPLE* newStream    = nullptr;
+	dirent*               folder_entry = nullptr;
+
+
+	// see if we have the music folder open
+	if (! music_dir) {
+		snprintf(path_buf, PATH_MAX, "%s/music", configDir);
+		music_dir = opendir(path_buf);
+		if (!music_dir)
+			return false;
+	}
+
+
+    // at this point we should have an open music folder
+    // the music folder is closed by global's deconstructor
+    // search for files ending in .wav
+	folder_entry = readdir(music_dir);
+	while (folder_entry && !newStream) {
+		// we have something, see if it is a wav file
+		if ( strstr(folder_entry->d_name, ".wav") ) {
+			snprintf(path_buf, PATH_MAX, "%s/music/%s",
+						configDir, folder_entry->d_name);
+			newStream = load_sample(path_buf);
+		}
+		if (!newStream)
+			folder_entry = readdir(music_dir);
+	}
+
+	if (!folder_entry)  {
+		// hit end of folder
+		closedir(music_dir);
+		music_dir = nullptr;
+
+		// If there is a current background music file loaded, then the
+		// directory is just gone through completely. In that case a
+		// recursive call re-opens the directory and starts anew.
+		if (!isSecondTry && background_music) {
+			isSecondTry = true;
+			return loadBackgroundMusic();
+		} else {
+			// Otherwise there is either an error or there are no
+			// files in that directory
+			if (background_music) {
+				// Okay, this is odd.
+				destroy_sample(background_music);
+				background_music = nullptr;
+			}
+			play_music = false;
+			return false;
+		}
+	}
+
+	if (background_music)
+		destroy_sample(background_music);
+	background_music = newStream;
+	isSecondTry      = false;
+
+	return true;
 }
 
-void ENVIRONMENT::make_fullUpdate()
+
+/*
+ * This function loads all the bitmaps needed by the game.
+ * Bitmaps are found in a series of sub-folders under the
+ * data directory. The function returns true on success and
+ * false if an error occurs.
+*/
+bool ENVIRONMENT::loadBitmaps()
 {
-  // Replace Updates with a full screen update:
-  int iOldCombUpd = combineUpdates;
-  combineUpdates = 0;
-
-  // They are splitted into 4 x 2 updates:
-  int iQuartWidth = _global->halfWidth / 2;
-  int iHalfHeight = _global->halfHeight;
-
-  _global->updateCount = 0;
-  for (int x = 0; x < 4; x++)
-    for (int y = 0; y < 2; y++)
-      make_update(iQuartWidth *		x		, iHalfHeight *		y,
-                  iQuartWidth * (x+1)	, iHalfHeight *	(y+1));
-
-  _global->lastUpdatesCount = 0;
-  for (int x = 0; x < 4; x++)
-    for (int y = 0; y < 2; y++)
-      make_bgupdate(iQuartWidth *		x		, iHalfHeight *		y,
-                    iQuartWidth * (x+1)	, iHalfHeight *	(y+1));
-
-  combineUpdates = iOldCombUpd;
+	int32_t  file_group    = 0;
+	char     sub_folder[9] = { 0 };
+	BITMAP*  newbitmap     = nullptr;
+	BITMAP** bitmap_array  = nullptr;
+
+	while (file_group < 7) {
+		// set the folder we're looking at
+		switch (file_group) {
+			case 0: strncpy(sub_folder, "title",   8); break;
+			case 1: strncpy(sub_folder, "button",  8); break;
+			case 2: strncpy(sub_folder, "misc",    8); break;
+			case 3: strncpy(sub_folder, "missile", 8); break;
+			case 4: strncpy(sub_folder, "stock",   8); break;
+			case 5: strncpy(sub_folder, "tank",    8); break;
+			case 6: strncpy(sub_folder, "tankgun", 8); break;
+		}
+
+		// set up empty array
+		int32_t array_size = 10;
+		bitmap_array = (BITMAP **) calloc(10, sizeof(BITMAP *) );
+		if (! bitmap_array) {
+			printf("Ran out of memory, loading bitmaps.\n");
+			return false;
+		}
+
+		// search for files
+		int32_t file_count = 0;
+		snprintf(path_buf, PATH_MAX, "%s/%s/%d.bmp", dataDir,
+				sub_folder, file_count);
+		while ( !access(path_buf, F_OK | R_OK) && bitmap_array ) {
+			newbitmap = load_bitmap(path_buf, nullptr);
+			if (! newbitmap)
+				printf("An error occured loading bitmap %s\n", path_buf);
+
+			// Crop tank bitmaps for unification
+			if (newbitmap && (5 == file_group)) {
+				int32_t left   = 0;
+				int32_t right  = newbitmap->w;
+				int32_t top    = 0;
+				int32_t bottom = newbitmap->h;
+
+				// Find real left edge
+				bool hasPix = false;
+				while (!hasPix && (left < right)) {
+					for (int32_t y = top; !hasPix && (y < bottom); ++y) {
+						if (PINK != getpixel(newbitmap, left, y))
+							hasPix = true;
+					}
+					if (!hasPix)
+						++left;
+				}
+
+				// Find real right edge
+				hasPix = false;
+				while (!hasPix && (right > left)) {
+					for (int32_t y = top; !hasPix && (y < bottom); ++y) {
+						if (PINK != getpixel(newbitmap, right, y))
+							hasPix = true;
+					}
+					if (!hasPix)
+						--right;
+				}
+
+				// Find real top edge
+				hasPix = false;
+				while (!hasPix && (top < bottom)) {
+					for (int32_t x = left; !hasPix && (x < right); ++x) {
+						if (PINK != getpixel(newbitmap, x, top))
+							hasPix = true;
+					}
+					if (!hasPix)
+						++top;
+				}
+
+				// Find real bottom edge
+				hasPix = false;
+				while (!hasPix && (bottom > top)) {
+					for (int32_t x = left; !hasPix && (x < right); ++x) {
+						if (PINK != getpixel(newbitmap, x, bottom))
+							hasPix = true;
+					}
+					if (!hasPix)
+						--bottom;
+				}
+
+				// Now create the real bitmap
+				bitmap_array[file_count] = create_bitmap(right - left,
+															bottom - top);
+				blit(newbitmap, bitmap_array[file_count], left, top, 0, 0,
+						right - left, bottom - top);
+
+				destroy_bitmap(newbitmap);
+			} // End of cropping tank bitmap
+
+			// otherwise just copy the bitmap pointer
+			else {
+				bitmap_array[file_count] = newbitmap;
+			}
+
+			file_count++;
+
+			// make sure array is large enough
+			if ( file_count >= array_size) {
+				array_size += 10;
+				bitmap_array = (BITMAP **) realloc(bitmap_array,
+									sizeof(BITMAP *) * (array_size + 1) );
+			if (! bitmap_array) {
+				printf("Unable to increase array size while loading bitmaps.\n");
+				return false;
+			} else
+				memset(bitmap_array + file_count, 0,
+						sizeof(BITMAP*) * (array_size - file_count));
+		}
+
+			// get next file
+			snprintf(path_buf, PATH_MAX, "%s/%s/%d.bmp", dataDir,
+						sub_folder, file_count);
+		}
+
+		// save the new array
+		switch (file_group) {
+			case 0: title   = bitmap_array; break;
+			case 1: button  = bitmap_array; break;
+			case 2: misc    = bitmap_array; break;
+			case 3: missile = bitmap_array; break;
+			case 4: stock   = bitmap_array; break;
+			case 5: tank    = bitmap_array; break;
+			case 6: tankgun = bitmap_array; break;
+		}
+
+		file_group++;
+	}
+
+	return true;
 }
 
 
+// This file loads in extra fonts the game requires.
+// Fonts should be stored in the datafolder. On
+// success the function returns true. When an
+// error occurs, it returns false.
+bool ENVIRONMENT::loadFonts()
+{
+	snprintf(path_buf, PATH_MAX, "%s/unicode.dat", dataDir);
 
+	main_font = load_font(path_buf, nullptr, nullptr);
 
+	if (main_font)
+		font = main_font;
+	else
+		printf("Unable to load font %s\n", path_buf);
 
-int ENVIRONMENT::getAvgBgColor(int aTopLeftX, int aTopLeftY, int aBotRightX, int aBotRightY, double aVelX, double aVelY)
-    {
-      // Coordinate sets:
-      int iLeftX	= aTopLeftX;
-      int iRightX	= aBotRightX;
-      int iCentX;	// Will be calculated later
-      int iTopY		=	aTopLeftY;
-      int iBottomY=	aBotRightY;
-      int iCentY; // Will be calculated later!
-
-      // Colors:
-      int iColorToLe, iColorToCe, iColorToRi;	// top row
-      int iColorCeLe, iColorCeCe, iColorCeRi; // center row
-      int iColorBoLe, iColorBoCe, iColorBoRi; // bottom row
-      int iRed = 0;
-      int iGreen = 0;
-      int iBlue = 0;
-
-      // Get the coordinates into range:
-      if (aTopLeftX 	< 1)													iLeftX = 1;
-      if (aTopLeftX 	> (_global->screenWidth - 2))	iLeftX = _global->screenWidth - 2;
-      if (aBotRightX	<	1)													iRightX= 1;
-      if (aBotRightX	>	(_global->screenWidth - 2))	iRightX= _global->screenWidth	-	2;
-      iCentX = (iLeftX + iRightX) / 2;
-
-      if (aTopLeftY		< (MENUHEIGHT + 1))						 	iTopY			= MENUHEIGHT + 1;
-      if (aTopLeftY		> (_global->screenHeight - 2))	iTopY			= _global->screenHeight - 2;
-      if (aBotRightY	< (MENUHEIGHT + 1))						 	iBottomY	= MENUHEIGHT + 1;
-      if (aBotRightY	> (_global->screenHeight - 2))	iBottomY	= _global->screenHeight - 2;
-      iCentY = (iTopY + iBottomY) / 2;
-
-      // Get Sky color or bg color, whatever fits:
-      /*---------------------
-         --- Left side ---
-      ---------------------*/
-      if (surface[iLeftX] <= iTopY)
-        {
-          // All infront of the surface:
-          iColorBoLe	=	getpixel(terrain, iLeftX,		iBottomY);
-          iColorCeLe	=	getpixel(terrain, iLeftX,		iCentY);
-          iColorToLe	=	getpixel(terrain, iLeftX,		iTopY);
-        }
-      else
-        {
-          // look where we are going...
-          if (surface[iLeftX] > iBottomY)
-            {
-              // Left part is guaranteed to be in front of the sky, get all three colors at once:
-              iColorBoLe	=	getpixel(sky, iLeftX,		iBottomY - MENUHEIGHT);
-              iColorCeLe	=	getpixel(sky, iLeftX,		iCentY - MENUHEIGHT);
-              iColorToLe	=	getpixel(sky, iLeftX,		iTopY - MENUHEIGHT);
-            }
-          else
-            {
-              // At least the bottom color is infront of the terrain
-              iColorBoLe	=	getpixel(terrain, iLeftX,		iBottomY);
-              if (surface[iLeftX] > iCentY)
-                {
-                  // ...but the other two are in front of the sky:
-                  iColorCeLe	=	getpixel(sky, iLeftX,		iCentY - MENUHEIGHT);
-                  iColorToLe	=	getpixel(sky, iLeftX,		iTopY - MENUHEIGHT);
-                }
-              else
-                {
-                  // Nope, they are in front of the terrain
-                  iColorCeLe	=	getpixel(terrain, iLeftX,		iCentY);
-                  iColorToLe	=	getpixel(terrain, iLeftX,		iTopY);
-                }
-            }
-        }
-      /*---------------------
-         --- The Center ---
-      ---------------------*/
-      if (surface[iCentX] <= iTopY)
-        {
-          // All infront of the surface:
-          iColorBoCe =	getpixel(terrain, iCentX,		iBottomY);
-          iColorCeCe =	getpixel(terrain, iCentX,		iCentY);
-          iColorToCe =	getpixel(terrain, iCentX,		iTopY);
-        }
-      else
-        {
-          // look where we are going...
-          if (surface[iCentX] > iBottomY)
-            {
-              // Left part is guaranteed to be in front of the sky, get all three colors at once:
-              iColorBoCe =	getpixel(sky, iCentX,		iBottomY - MENUHEIGHT);
-              iColorCeCe =	getpixel(sky, iCentX,		iCentY - MENUHEIGHT);
-              iColorToCe =	getpixel(sky, iCentX,		iTopY - MENUHEIGHT);
-            }
-          else
-            {
-              // At least the bottom color is infront of the terrain
-              iColorBoCe =	getpixel(terrain, iCentX,		iBottomY);
-              if (surface[iCentX] > iCentY)
-                {
-                  // ...but the other two are in front of the sky:
-                  iColorCeCe =	getpixel(sky, iCentX,		iCentY - MENUHEIGHT);
-                  iColorToCe =	getpixel(sky, iCentX,		iTopY - MENUHEIGHT);
-                }
-              else
-                {
-                  // Nope, they are in front of the terrain
-                  iColorCeCe =	getpixel(terrain, iCentX,		iCentY);
-                  iColorToCe =	getpixel(terrain, iCentX,		iTopY);
-                }
-            }
-        }
-      /*----------------------
-         --- Right side ---
-      ----------------------*/
-      if (surface[iRightX] <= iTopY)
-        {
-          // All infront of the surface:
-          iColorBoRi =	getpixel(terrain, iRightX,		iBottomY);
-          iColorCeRi =	getpixel(terrain, iRightX,		iCentY);
-          iColorToRi =	getpixel(terrain, iRightX,		iTopY);
-        }
-      else
-        {
-          // look where we are going...
-          if (surface[iRightX] > iBottomY)
-            {
-              // Left part is guaranteed to be in front of the sky, get all three colors at once:
-              iColorBoRi =	getpixel(sky, iRightX,		iBottomY - MENUHEIGHT);
-              iColorCeRi =	getpixel(sky, iRightX,		iCentY - MENUHEIGHT);
-              iColorToRi =	getpixel(sky, iRightX,		iTopY - MENUHEIGHT);
-            }
-          else
-            {
-              // At least the bottom color is infront of the terrain
-              iColorBoRi =	getpixel(terrain, iRightX,		iBottomY);
-              if (surface[iRightX] > iCentY)
-                {
-                  // ...but the other two are in front of the sky:
-                  iColorCeRi =	getpixel(sky, iRightX,		iCentY - MENUHEIGHT);
-                  iColorToRi =	getpixel(sky, iRightX,		iTopY - MENUHEIGHT);
-                }
-              else
-                {
-                  // Nope, they are in front of the terrain
-                  iColorCeRi =	getpixel(terrain, iRightX,		iCentY);
-                  iColorToRi =	getpixel(terrain, iRightX,		iTopY);
-                }
-            }
-        }
+	// Store font height
+	if (main_font)
+		fontHeight = text_height(main_font);
 
-      // Fetch the rgb parts, according to movement:
-      /* --- X-Movement --- */
-      if (aVelX < 0.0)
-        {
-          // Movement to the left, weight left side color twice
-          iRed		+=	(_GET_R(iColorBoLe) * 2) + (_GET_R(iColorCeLe) * 2) + (_GET_R(iColorToLe) * 2);
-          iGreen	+=	(_GET_G(iColorBoLe) * 2) + (_GET_G(iColorCeLe) * 2) + (_GET_G(iColorToLe) * 2);
-          iBlue		+=	(_GET_B(iColorBoLe) * 2) + (_GET_B(iColorCeLe) * 2) + (_GET_B(iColorToLe) * 2);
-          // The others are counted once
-          iRed		+=	_GET_R(iColorBoCe) + _GET_R(iColorCeCe) + _GET_R(iColorToCe) + _GET_R(iColorBoRi) + _GET_R(iColorCeRi) + _GET_R(iColorToRi);
-          iGreen	+=	_GET_G(iColorBoCe) + _GET_G(iColorCeCe) + _GET_G(iColorToCe) + _GET_G(iColorBoRi) + _GET_G(iColorCeRi) + _GET_G(iColorToRi);
-          iBlue		+=	_GET_B(iColorBoCe) + _GET_B(iColorCeCe) + _GET_B(iColorToCe) + _GET_B(iColorBoRi) + _GET_B(iColorCeRi) + _GET_B(iColorToRi);
-        }
-      if (aVelX == 0.0)
-        {
-          // No X-Movement, weight center twice
-          iRed		+=	(_GET_R(iColorBoCe) * 2) + (_GET_R(iColorCeCe) * 2) + (_GET_R(iColorToCe) * 2);
-          iGreen	+=	(_GET_G(iColorBoCe) * 2) + (_GET_G(iColorCeCe) * 2) + (_GET_G(iColorToCe) * 2);
-          iBlue		+=	(_GET_B(iColorBoCe) * 2) + (_GET_B(iColorCeCe) * 2) + (_GET_B(iColorToCe) * 2);
-          // The others are counted once
-          iRed		+=	_GET_R(iColorBoLe) + _GET_R(iColorCeLe) + _GET_R(iColorToLe) + _GET_R(iColorBoRi) + _GET_R(iColorCeRi) + _GET_R(iColorToRi);
-          iGreen	+=	_GET_G(iColorBoLe) + _GET_G(iColorCeLe) + _GET_G(iColorToLe) + _GET_G(iColorBoRi) + _GET_G(iColorCeRi) + _GET_G(iColorToRi);
-          iBlue		+=	_GET_B(iColorBoLe) + _GET_B(iColorCeLe) + _GET_B(iColorToLe) + _GET_B(iColorBoRi) + _GET_B(iColorCeRi) + _GET_B(iColorToRi);
-        }
-      if (aVelX > 0.0)
-        {
-          // Movement to the right, weight right side color twice
-          iRed		+=	(_GET_R(iColorBoRi) * 2) + (_GET_R(iColorCeRi) * 2) + (_GET_R(iColorToRi) * 2);
-          iGreen	+=	(_GET_G(iColorBoRi) * 2) + (_GET_G(iColorCeRi) * 2) + (_GET_G(iColorToRi) * 2);
-          iBlue		+=	(_GET_B(iColorBoRi) * 2) + (_GET_B(iColorCeRi) * 2) + (_GET_B(iColorToRi) * 2);
-          // The others are counted once
-          iRed		+=	_GET_R(iColorBoCe) + _GET_R(iColorCeCe) + _GET_R(iColorToCe) + _GET_R(iColorBoLe) + _GET_R(iColorCeLe) + _GET_R(iColorToLe);
-          iGreen	+=	_GET_G(iColorBoCe) + _GET_G(iColorCeCe) + _GET_G(iColorToCe) + _GET_G(iColorBoLe) + _GET_G(iColorCeLe) + _GET_G(iColorToLe);
-          iBlue		+=	_GET_B(iColorBoCe) + _GET_B(iColorCeCe) + _GET_B(iColorToCe) + _GET_B(iColorBoLe) + _GET_B(iColorCeLe) + _GET_B(iColorToLe);
-        }
-      /* --- Y-Movement --- */
-      if (aVelY < 0.0)
-        {
-          // Movement upwards, weight upper side color twice
-          iRed		+=	(_GET_R(iColorToLe) * 2) + (_GET_R(iColorToCe) * 2) + (_GET_R(iColorToRi) * 2);
-          iGreen	+=	(_GET_G(iColorToLe) * 2) + (_GET_G(iColorToCe) * 2) + (_GET_G(iColorToRi) * 2);
-          iBlue		+=	(_GET_B(iColorToLe) * 2) + (_GET_B(iColorToCe) * 2) + (_GET_B(iColorToRi) * 2);
-          // The others are counted once
-          iRed		+=	_GET_R(iColorBoLe) + _GET_R(iColorCeLe) + _GET_R(iColorBoCe) + _GET_R(iColorCeCe) + _GET_R(iColorBoRi) + _GET_R(iColorCeRi);
-          iGreen	+=	_GET_G(iColorBoLe) + _GET_G(iColorCeLe) + _GET_G(iColorBoCe) + _GET_G(iColorCeCe) + _GET_G(iColorBoRi) + _GET_G(iColorCeRi);
-          iBlue		+=	_GET_B(iColorBoLe) + _GET_B(iColorCeLe) + _GET_B(iColorBoCe) + _GET_B(iColorCeCe) + _GET_B(iColorBoRi) + _GET_B(iColorCeRi);
-        }
-      if (aVelY == 0.0)
-        {
-          // No Y-Movement, weight center twice
-          iRed		+=	(_GET_R(iColorCeLe) * 2) + (_GET_R(iColorCeCe) * 2) + (_GET_R(iColorCeRi) * 2);
-          iGreen	+=	(_GET_G(iColorCeLe) * 2) + (_GET_G(iColorCeCe) * 2) + (_GET_G(iColorCeRi) * 2);
-          iBlue		+=	(_GET_B(iColorCeLe) * 2) + (_GET_B(iColorCeCe) * 2) + (_GET_B(iColorCeRi) * 2);
-          // The others are counted once
-          iRed		+=	_GET_R(iColorBoLe) + _GET_R(iColorToLe) + _GET_R(iColorBoCe) + _GET_R(iColorToCe) + _GET_R(iColorBoRi) + _GET_R(iColorToRi);
-          iGreen	+=	_GET_G(iColorBoLe) + _GET_G(iColorToLe) + _GET_G(iColorBoCe) + _GET_G(iColorToCe) + _GET_G(iColorBoRi) + _GET_G(iColorToRi);
-          iBlue		+=	_GET_B(iColorBoLe) + _GET_B(iColorToLe) + _GET_B(iColorBoCe) + _GET_B(iColorToCe) + _GET_B(iColorBoRi) + _GET_B(iColorToRi);
-        }
-      if (aVelY > 0.0)
-        {
-          // Movement downwards, weight lower side color twice
-          iRed		+=	(_GET_R(iColorBoRi) * 2) + (_GET_R(iColorBoCe) * 2) + (_GET_R(iColorBoLe) * 2);
-          iGreen	+=	(_GET_G(iColorBoRi) * 2) + (_GET_G(iColorBoCe) * 2) + (_GET_G(iColorBoLe) * 2);
-          iBlue		+=	(_GET_B(iColorBoRi) * 2) + (_GET_B(iColorBoCe) * 2) + (_GET_B(iColorBoLe) * 2);
-          // The others are counted once
-          iRed		+=	_GET_R(iColorToLe) + _GET_R(iColorCeLe) + _GET_R(iColorToCe) + _GET_R(iColorCeCe) + _GET_R(iColorToRi) + _GET_R(iColorCeRi);
-          iGreen	+=	_GET_G(iColorToLe) + _GET_G(iColorCeLe) + _GET_G(iColorToCe) + _GET_G(iColorCeCe) + _GET_G(iColorToRi) + _GET_G(iColorCeRi);
-          iBlue		+=	_GET_B(iColorToLe) + _GET_B(iColorCeLe) + _GET_B(iColorToCe) + _GET_B(iColorCeCe) + _GET_B(iColorToRi) + _GET_B(iColorCeRi);
-        }
-      /* I know this looks weird, but what we now have is some kind of summed matrix, which is always the same:
-       * Let's assume that dVelX and dVelY are both 0.0, so no movement is happening. The result is: (In counted times)
-       * 2|3|2  ( =  7)
-       * -+-+-
-       * 3|4|3  ( = 10)
-       * -+-+-
-       * 2|3|2  ( =  7)
-       *          = 24
-       * And it is always 24, no matter which movement combination you try. */
-      iRed 	/= 24;
-      if (iRed	> 0xff)	iRed	= 0xff;
-      iGreen/= 24;
-      if (iGreen> 0xff)	iGreen= 0xff;
-      iBlue /= 24;
-      if (iBlue	> 0xff)	iBlue	= 0xff;
-
-      return(makecol(iRed, iGreen, iBlue));
-    }
+	return main_font ? true : false;
+}
 
 
+/// @brief collection of all game text file loadings.
+bool ENVIRONMENT::loadGameFiles()
+{
+	// Before the (language specific) weapons texts can be loaded,
+	// the english one must be pre-loaded to get the weapons data.
+	// all other files only hold the texts.
+	bool status = true;
+	if (EL_ENGLISH != language) {
+        eLanguages cur_lang = language;
+        language = EL_ENGLISH;
+		status   = Load_Weapons_Text();
+        language = cur_lang;
+	}
+
+	if (status)
+		status = Load_Weapons_Text();
+
+	if (!status) {
+		cerr << "ERROR: An error occurred trying to read weapons file." << endl;
+		return status;
+	}
+	// Note: If english is chosen, the first load is not done.
+	//       If english is not chosen, the first load pre-loads english
+	//       Thus the second load is always necessary.
+
+
+	bitmap_filenames = Find_Bitmaps(&number_of_bitmaps);
+
+	// If no bitmaps where found, a custom background is futile.
+	if ( custom_background
+	  && !bitmap_filenames)
+		custom_background = 0;
+
+	Create_Music_Folder();
+	genItemsList ();
+
+	return status;
+}
 
 
-/*
- * This function puts all the of the environment settings back
- * to their default values. These are settings which get written
- * to the config file.
- * -- Jesse
- *  */ 
-void ENVIRONMENT::Reset_Options()
+/** @brief load all needed sounds
+  * This function loads all sounds from the data folder and saves them
+  * in an array.
+  * @return true on success or false if an error happens.
+**/
+bool ENVIRONMENT::loadSounds()
 {
-  windstrength = 8;
-  windvariation = 1;
-  viscosity = 0.5;
-  gravity = 0.150;
-  weapontechLevel = 5;
-  itemtechLevel = 5;
-  meteors = 0;
-  lightning = 0;
-  satellite = 0.0;
-  fog = 0;
-  landType = LANDTYPE_RANDOM;
-  landSlideType = LANDSLIDE_GRAVITY;
-  landSlideDelay = MAX_GRAVITY_DELAY;
-  falling_dirt_balls = 0.0;
-  wallType = 0.0;
-  dBoxedMode = 0.0;
-  dFadingText = 0.0;
-  dShadowedText = 0.0;
-  custom_background = 0.0;
+	SAMPLE *temp_sample = nullptr;
+
+	// allocate space for sound samples
+	sounds = (SAMPLE **) calloc(SND_COUNT, sizeof(SAMPLE *) );
+	if (! sounds) {
+		printf("Unable to create sound array.\n");
+		return false;
+	}
+
+	// read from directory
+	for (int32_t i = 0; i < SND_COUNT; ++i) {
+		snprintf(path_buf, PATH_MAX, "%s/sound/%02d.wav", dataDir, i);
+		if (!access(path_buf, R_OK)) {
+			temp_sample = load_sample(path_buf);
+			if (temp_sample)
+				sounds[i] = temp_sample;
+			else
+				fprintf(stderr, "An error occured loading sound file %s\n", path_buf);
+		}
+		// No else, because the sound enum has free slots.
+	}
+
+   return true;
 }
 
 
-/*
-This function checks to see which wall type we are using
-and sets the appropriate colour.
-*/
-void ENVIRONMENT::Set_Wall_Colour()
+void ENVIRONMENT::newRound ()
 {
+	// set wall type
+	if (wallType == WALL_RANDOM)
+		current_wallType = rand() % 4;
+	else
+		current_wallType = wallType;
+
+	time_to_fall = (rand() & landSlideDelay) + 1;
+
+	// Set boxed mode
+	if (BM_RANDOM == boxedMode) {
+		if (rand() % 2)
+			isBoxed = true;
+		else
+			isBoxed = false;
+	} else if (BM_ON == boxedMode)
+		isBoxed = true;
+	else
+		isBoxed = false;
+
+	// Set wall colour
     switch (current_wallType)
     {
        case WALL_RUBBER:
@@ -1119,80 +1333,237 @@ void ENVIRONMENT::Set_Wall_Colour()
        case WALL_WRAP:
           wallColour = makecol(255, 255, 0); break;
     }
-}
 
+	// Init player array
+	for (int32_t i = 0; i < MAXPLAYERS; ++i)
+		playerOrder[i] = nullptr;
+}
 
 
-bool ENVIRONMENT::Get_Boxed_Mode(GLOBALDATA *global)
+void ENVIRONMENT::removeGamePlayer(PLAYER* player_)
 {
-    if (dBoxedMode == 2.0)
-    {
-       if (rand() % 2)
-          global->bIsBoxed = true;
-       else
-          global->bIsBoxed = false;
-    }
-    else if (dBoxedMode == 1.0)
-         global->bIsBoxed = true;
-    else if (dBoxedMode == 0.0)
-         global->bIsBoxed = false;
-    return global->bIsBoxed;
-} 
-
+	int32_t fromCount = 0;
+	int32_t toCount = -1;
+
+	if (HUMAN_PLAYER == player_->type)
+		numHumanPlayers--;
+
+	while (fromCount < numGamePlayers) {
+		if (player_ != players[fromCount]) {
+			if ((toCount >= 0) && (fromCount > toCount)) {
+				players[toCount]    = players[fromCount];
+				players[fromCount]  = nullptr;
+				toCount++;
+			}
+		} else
+			// Position found, now move the remaining players down!
+			toCount = fromCount;
+		fromCount++;
+	}
+	numGamePlayers--;
+}
 
 
-TANK *ENVIRONMENT::Get_Next_Tank(int *wrapped_around)
+/*
+ * This function puts all the of the environment settings back
+ * to their default values. These are settings which get written
+ * to the config file.
+ * -- Jesse
+ *  */
+void ENVIRONMENT::Reset_Options()
 {
-   int index;
-   int found = FALSE;
-   int old_index;
-   int wrapped = 0;
-
-   index = global_tank_index;
-   old_index = index;
-   index++;
-   while (!found)
-   {
-        if (index >= MAXPLAYERS)
-        {
-             index = 0;
-             *wrapped_around = TRUE;
-             wrapped++;
-        }
-        if ( (order[index]) && (index != old_index) )
-        {
-             found = TRUE;
-        }
-        else
-           index++;
-        if (wrapped > 1)
-           break;
-   }
-
-   /* 
-   if (! found)
-      *wrapped_around = TRUE;
-   else
-      *wrapped_around = FALSE;
-   */
-   global_tank_index = index;
-   return order[index];
+	boxedMode             = BM_OFF;
+	check_for_updates     = true;
+	colourTheme           = CT_CRISPY;
+	custom_background     = 0;
+	debris_level          = 1;
+	detailedLandscape     = false;
+	detailedSky           = false;
+	ditherGradients       = true;
+	divide_money          = false;
+	fadingText            = false;
+	falling_dirt_balls    = 0;
+	fog                   = 0;
+	set_fps(60);
+	gravity               = 0.15;
+	interest              = 1.25;
+	itemtechLevel         = 5;
+	landSlideDelay        = MAX_GRAVITY_DELAY;
+	landSlideType         = SLIDE_GRAVITY;
+	landType              = LAND_RANDOM;
+	language              = EL_ENGLISH;
+	lightning             = 0;
+	maxFireTime           = 0;
+	meteors               = 0;
+	network_enabled       = false;
+	network_port          = DEFAULT_NETWORK_PORT;
+	osMouse               = true;
+	play_music            = 1.0;
+	satellite             = 0;
+	scoreHitUnit          = 75;
+	scoreRoundWinBonus    = 10000;
+	scoreSelfHit          = 25;
+	scoreTeamHit          = 10;
+	scoreUnitDestroyBonus = 5000;
+	scoreUnitSelfDestroy  = 0;
+	sellpercent           = 0.80;
+	shadowedText          = true;
+	skipComputerPlay      = SKIP_HUMANS_DEAD;
+	sound_driver          = SD_AUTODETECT;
+	sound_enabled         = true;
+	startmoney            = 15000;
+	swayingText           = true;
+	temp_screenHeight     = DEFAULT_SCREEN_HEIGHT;
+	temp_screenWidth      = DEFAULT_SCREEN_WIDTH;
+	turntype              = TURN_RANDOM;
+	viscosity             = 0.5;
+	violent_death         = 0;
+	volley_delay          = 10;
+	volume_factor         = MAX_VOLUME_FACTOR;
+	wallType              = WALL_RUBBER;
+	weapontechLevel       = 5;
+	windstrength          = 8;
+	windvariation         = 1;
+
+	strncpy(server_name, "127.0.0.1", 127);
+	strncpy(server_port, "25645", 127);
 }
 
-void ENVIRONMENT::increaseVolume()
+
+/** @brief Save environment settings to a text file
+  *
+  * This function saves the environment settings to a text file. Each line has
+  * the format name=value.\n
+  *
+  * @return true on success and false on failure.
+*/
+bool ENVIRONMENT::save_to_file (FILE *file)
 {
-  if (volume_factor < MAX_VOLUME_FACTOR)
-    ++volume_factor;
+	if (!file)
+		return false;
+
+	fprintf (file, "*ENV*\n");
+
+	fprintf (file, "ACCELERATEDAI=%d\n", skipComputerPlay);
+	fprintf (file, "BOXMODE=%d\n", boxedMode);
+	fprintf (file, "CHECKUPDATES=%d\n", check_for_updates ? 1 : 0);
+	fprintf (file, "COLOURTHEME=%d\n", colourTheme);
+	fprintf (file, "CUSTOMBACKGROUND=%d\n", custom_background);
+	fprintf (file, "DEBRISLEVEL=%d\n", debris_level);
+	fprintf (file, "DETAILEDLAND=%d\n", detailedLandscape ? 1 : 0);
+	fprintf (file, "DETAILEDSKY=%d\n", detailedSky ? 1 : 0);
+	fprintf (file, "DITHER=%d\n", ditherGradients ? 1 : 0);
+	fprintf (file, "DIVIDEMONEY=%d\n", divide_money);
+	fprintf (file, "DOBOXWRAP=%d\n", do_box_wrap);
+	fprintf (file, "DYNAMICMENUBG=%d\n", dynamicMenuBg ? 1 : 0);
+	fprintf (file, "FALLINGDIRTBALLS=%d\n", falling_dirt_balls);
+	fprintf (file, "FOG=%d\n", fog);
+	fprintf (file, "FRAMES=%d\n", frames_per_second);
+	fprintf (file, "FULLSCREEN=%d\n", full_screen);
+	fprintf (file, "GRAVITY=%f\n", gravity);
+	fprintf (file, "INTEREST=%f\n", interest);
+	fprintf (file, "ITEMTECHLEVEL=%d\n", itemtechLevel);
+	fprintf (file, "LANDSLIDEDELAY=%d\n", landSlideDelay);
+	fprintf (file, "LANDSLIDETYPE=%d\n", landSlideType);
+	fprintf (file, "LANDTYPE=%d\n", landType);
+	fprintf (file, "LANGUAGE=%u\n", static_cast<uint32_t>(language));
+	fprintf (file, "LIGHTNING=%d\n", lightning);
+	fprintf (file, "MAXFIRETIME=%d\n", maxFireTime);
+	fprintf (file, "METEORS=%d\n", meteors);
+	fprintf (file, "NETWORKING=%d\n", network_enabled ? 1 : 0);
+	fprintf (file, "NETWORKPORT=%d\n", network_port);
+	fprintf (file, "NUMPERMANENTPLAYERS=%d\n", numPermanentPlayers);
+	fprintf (file, "OSMOUSE=%d\n", osMouse);
+	fprintf (file, "PLAYMUSIC=%d\n", play_music ? 1 : 0);
+	fprintf (file, "ROUNDS=%d\n", rounds);
+	fprintf (file, "SATELLITE=%d\n", satellite);
+	fprintf (file, "SCOREHITUNIT=%d\n", scoreHitUnit);
+	fprintf (file, "SCOREROUNDWINBONUS=%d\n", scoreRoundWinBonus);
+	fprintf (file, "SCORESELFHIT=%d\n", scoreSelfHit);
+	fprintf (file, "SCORETEAMHIT=%d\n", scoreTeamHit);
+	fprintf (file, "SCOREUNITDESTROYBONUS=%d\n", scoreUnitDestroyBonus);
+	fprintf (file, "SCOREUNITSELFDESTROY=%d\n", scoreUnitSelfDestroy);
+	fprintf (file, "SCREENHEIGHT=%d\n", temp_screenHeight);
+	fprintf (file, "SCREENWIDTH=%d\n", temp_screenWidth);
+	fprintf (file, "SELLPERCENT=%f\n", sellpercent);
+	fprintf (file, "SERVERNAME='%s'\n", server_name);
+	fprintf (file, "SERVERPORT='%s'\n", server_port);
+	fprintf (file, "SHOWAIFEEDBACK=%d\n", showAIFeedback ? 1 : 0);
+	fprintf (file, "SHOWFPS=%d\n", showFPS ? 1 : 0);
+	fprintf (file, "SOUNDDRIVER=%d\n", sound_driver);
+	fprintf (file, "SOUNDENABLED=%d\n", sound_enabled ? 1 : 0);
+	fprintf (file, "STARTMONEY=%d\n", startmoney);
+	fprintf (file, "TEXTFADE=%d\n", fadingText ? 1 : 0);
+	fprintf (file, "TEXTSHADOW=%d\n", shadowedText ? 1 : 0);
+	fprintf (file, "TEXTSWAY=%d\n", swayingText ? 1 : 0);
+	fprintf (file, "TURNTYPE=%d\n", turntype);
+	fprintf (file, "VISCOSITY=%f\n", viscosity);
+	fprintf (file, "VIOLENTDEATH=%d\n", violent_death);
+	fprintf (file, "VOLLEYDELAY=%d\n", volley_delay);
+	fprintf (file, "VOLUMEFACTOR=%d\n", volume_factor);
+	fprintf (file, "WALLTYPE=%d\n", wallType);
+	fprintf (file, "WEAPONTECHLEVEL=%d\n", weapontechLevel);
+	fprintf (file, "WINDSTRENGTH=%d\n", windstrength);
+	fprintf (file, "WINDVARIATION=%d\n", windvariation);
+	fprintf (file, "***\n");
+
+	return true;
 }
 
-void ENVIRONMENT::decreaseVolume()
+
+/// @brief This function sends a message to all connected game clients.
+/// @return true on success or false if the message could not be sent
+bool ENVIRONMENT::sendToClients(const char* message)
 {
-  if (volume_factor > 0)
-    --volume_factor;
+	if (! message) return false;
+
+#ifdef NETWORK
+	int32_t written = 0;
+	int32_t message_length = strlen(message);
+
+	for (int32_t index = 0; index < numGamePlayers; index++) {
+		if ( (players[index]) && (players[index]->type == NETWORK_CLIENT) ) {
+			written = write(players[index]->server_socket,
+							message, message_length);
+			if (written < message_length)
+				fprintf(stderr, "%s:%d: Warning: Only %d/%d bytes sent to player %d\n",
+						__FILE__, __LINE__, written, message_length, index);
+		}
+	} // done all players
+#endif // NETWORK
+	return true;
 }
 
-int ENVIRONMENT::scaleVolume(int vol) const
+
+
+/// @brief set new frames per second if valid and calculate dependent values.
+void ENVIRONMENT::set_fps(int32_t new_FPS)
 {
-    return (vol * volume_factor) / MAX_VOLUME_FACTOR;
+	if (!new_FPS || ((new_FPS > 0) && (new_FPS != frames_per_second)) ) {
+		if (new_FPS)
+			frames_per_second = new_FPS;
+		FPS_mod           = 100. / static_cast<double>(frames_per_second);
+		maxVelocity       = static_cast<double>(MAX_POWER) * FPS_mod / 100.;
+	}
 }
 
+
+void ENVIRONMENT::window_update(int32_t x, int32_t y, int32_t w, int32_t h)
+{
+	if (x < window.x)
+		window.x = x;
+	if (y < window.y)
+		window.y = y;
+	if (x + w > window.w)
+		window.w = (x + w) - 1;
+	if (y + h > window.h)
+		window.h = (y + h) - 1;
+	if (window.x < 0)
+		window.x = 0;
+	if (window.y < MENUHEIGHT)
+		window.y = MENUHEIGHT;
+	if (window.w > (screenWidth-1))
+		window.w = (screenWidth-1);
+	if (window.h > (screenHeight-1))
+		window.h = (screenHeight-1);
+}
diff --git a/src/environment.h b/src/environment.h
index f4fe553..5934ee3 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -22,169 +22,263 @@
 
 
 #include "main.h"
+#include "network.h"
+#include "gfxData.h"
+#include "text.h"
 
-enum landscapeTypes
-{
-  LANDTYPE_RANDOM,
-  LANDTYPE_CANYONS,
-  LANDTYPE_MOUNTAINS,
-  LANDTYPE_VALLEYS,
-  LANDTYPE_HILLS,
-  LANDTYPE_FOOTHILLS,
-  LANDTYPE_PLAIN,
-  LANDTYPE_NONE
-};
 
-enum landSlideTypes
-{
-  LANDSLIDE_NONE,       // gravity does not exist
-  LANDSLIDE_TANK_ONLY,  // dirt falls, tank does not
-  LANDSLIDE_INSTANT,    // dirt falls without you seeing it
-  LANDSLIDE_GRAVITY,    // normal
-  LANDSLIDE_CARTOON     // gravity is delayed
-};
+// As everything depends on environment.h, PLAYER, TANK and VIRTUAL_OBJECT
+// Must be forwarded here, and included before the ENVIRONMENT definition
+class VIRTUAL_OBJECT;
+class TANK;
+class PLAYER;
+
+
+#ifndef HAS_DIRENT
+#  if defined(ATANKS_IS_MSVC)
+#    include "extern/dirent.h"
+#  else
+#    include <dirent.h>
+#  endif // Linux
+#  define HAS_DIRENT 1
+#endif //HAS_DIRENT
+
 
 #ifndef MAX_GRAVITY_DELAY
-#define GRAVITY_DELAY 200
-#define MAX_GRAVITY_DELAY 3
+#  define GRAVITY_DELAY 200
+#  define MAX_GRAVITY_DELAY 3
 #endif
 
-enum wallTypes
-{
-  WALL_RUBBER,
-  WALL_STEEL,
-  WALL_SPRING,
-  WALL_WRAP,
-  WALL_RANDOM
-};
+#define SPRING_CHANGE 1.25
+#define BOUNCE_CHANGE 0.90
 
+#define GET_R(x) ((x & 0xff0000) >> 16)
+#define GET_G(x) ((x & 0x00ff00) >> 8)
+#define GET_B(x) ( x & 0x0000ff)
 
-// stages
-#define STAGE_AIM 0
-#define STAGE_FIRE 1
-#define STAGE_ENDGAME 3
 
+// Something from externs.h can not be used via include
+// due to circular dependencies.
+extern int32_t GREY;
+extern int32_t GREEN;
+
+// Defined in sound.cpp:
+extern int32_t MAX_VOLUME_FACTOR;
 
-#define SPRING_CHANGE 1.25
 
-// size of satellite laser
-#define LASER_NONE 0.0
-#define LASER_WEAK 1.0
-#define LASER_STRONG 2.0
-#define LASER_SUPER 3.0
-
-#define _GET_R(x) ((x & 0xff0000) >> 16)
-#define _GET_G(x) ((x & 0x00ff00) >> 8)
-#define _GET_B(x) (x & 0x0000ff)
-
-// max volume factor: means that the interval 0% -> 100% is splitted in 5
-#define MAX_VOLUME_FACTOR 5
-
-#include "tank.h"
-//class TANK;
-//class MISSILE;
-//class FLOATTEXT;
-//class GLOBALDATA;
-//class VIRTUAL_OBJECT;
+/** @class ENVIRONMENT
+  * @brief Fixed values of the current environment the game takes place in.
+  *
+  * This class holds all values and the corresponding methods that define
+  * the gaming environment.
+  *
+  * This means that all values in here must be set on game round start and
+  * must not change until the game round ends.
+  *
+  * So basically this class consolidates everything set up with the options
+  * menu and by the game round initialization.
+  *
+  * Everything that can change between the game round start and the game round
+  * end has to be managed by GLOBALDATA.
+**/
 class ENVIRONMENT
-  {
-  public:
-    GLOBALDATA	*_global;
-    TANK	*order[MAXPLAYERS * 3];
-    VIRTUAL_OBJECT	*objects[MAX_OBJECTS];
-    int	availableItems[THINGS];
-    int	numAvailable;
-    int	realm, am;
-    double	wind, lastwind;
-    double	windstrength, windvariation;
-    double	viscosity;
-    double	gravity;
-    double	weapontechLevel, itemtechLevel;
-    double	meteors;
-    double	lightning;
-    double  falling_dirt_balls;
-    double	fog;
-    // int	oldFogX, oldFogY;
-    double	landType;
-    double	landSlideType;
-    double  landSlideDelay;
-    char	*done;
-    int	*fp;
-    int	*h;
-    int	*surface;
-    int	*dropTo;
-    double	*velocity;
-    double	*dropIncr;
-    double	*height;
-    BITMAP	*db, *terrain, *sky;
-    BITMAP *waiting_sky, *waiting_terrain;     // sky saved in background
-    #ifdef THREADS
-    pthread_mutex_t* waiting_terrain_lock;
-    pthread_mutex_t* waiting_sky_lock;
-    void destroy_lock(pthread_mutex_t* lock);
-    pthread_mutex_t* init_lock(pthread_mutex_t* lock);
-    void lock(pthread_mutex_t* lock);
-    void unlock(pthread_mutex_t* lock);
-    #endif
-    BITMAP* get_waiting_sky();
-    BITMAP* get_waiting_terrain();
-    const gradient **my_sky_gradients, **my_land_gradients;
-    int	pclock, mouseclock;
-    int	stage;
-    int	combineUpdates;
-    double  wallType;
-    double	dBoxedMode;			// Can be 0.0 = off, 1.0 = on, 2.0 = random
-    double	dFadingText;		// 0.0 = off, 1.0 = on
-    double	dShadowedText;	// 0.0 = off, 1.0 = on
-    int     current_wallType, wallColour;
-    int     time_to_fall;    // amount of time dirt will hover
-    double  satellite;
-    int     naturals_since_last_shot;
-    double custom_background;
-    char **bitmap_filenames;
-    int number_of_bitmaps;
-    int current_drawing_mode;
-    int global_tank_index;
-    int volume_factor;  // from 0 (no volume) to MAX_VOLUME_FACTOR
-
-
-    ENVIRONMENT (GLOBALDATA *global);
-    ~ENVIRONMENT ();
-    void initialise ();
-    void setGlobaldata (GLOBALDATA *global)
-    {
-      _global = global;
-    }
-    GLOBALDATA *Get_Globaldata()
-    {
-         return _global;
-    }
-    void generateAvailableItemsList ();
-    int isItemAvailable (int itemNum);
-    int addObject (VIRTUAL_OBJECT *object);
-    int removeObject (VIRTUAL_OBJECT *object);
-    VIRTUAL_OBJECT *getNextOfClass (int classNum, int *index);
-    void newRound ();
-    int ingamemenu ();
-    void make_fullUpdate();
-    int getAvgBgColor(int, int, int, int, double, double); 
-    void do_updates ();
-    void replaceCanvas ();
-    void make_update (int x, int y, int w, int h);
-    void make_bgupdate (int x, int y, int w, int h);
-
-    int saveToFile_Text (FILE *file);
-    int loadFromFile_Text (FILE *file);
-    int loadFromFile (ifstream &ifsFile);
-
-    void Reset_Options();
-    void Set_Wall_Colour();
-    bool Get_Boxed_Mode(GLOBALDATA *global);
-    TANK *Get_Next_Tank(int *wrapped_around);
-    void increaseVolume();
-    void decreaseVolume();
-    int scaleVolume(int vol) const;
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+	explicit ENVIRONMENT ();
+	~ENVIRONMENT ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+	void     addGamePlayer      (PLAYER* player_);
+	PLAYER*  createNewPlayer    (const char* player_name);
+	void     creditWinners      (int32_t winner);
+	void     decreaseVolume     ();
+	void     deletePermPlayer   (PLAYER* player_);
+	void     destroy            (); // Must be called before allegro shutdown
+	void     find_config_dir    ();
+	bool     find_data_dir      ();
+	void     first_init         (); // Used for the first init after creation
+	void     genItemsList       ();
+	int32_t  getPlayerByName    (const char* player_name);
+	void     increaseVolume     ();
+	int32_t  ingamemenu         ();
+	void     initialise         (); // Does a regular initialization
+	bool     isItemAvailable    (int32_t itemNum);
+	bool     loadBackgroundMusic();
+	bool     loadBitmaps        ();
+	bool     loadFonts          ();
+	bool     loadGameFiles      ();
+	void     load_from_file     (FILE *file);
+	bool     loadSounds         ();
+	void     load_text_files    ();
+	void     newRound           ();
+	void     removeGamePlayer   (PLAYER* player_);
+	void     Reset_Options      ();
+	bool     save_to_file       (FILE *file);
+	bool     sendToClients      (const char* message); // send a short message to all network clients
+	void     set_fps            (int32_t new_FPS);
+	void     window_update      (int32_t x, int32_t y, int32_t w, int32_t h);
+
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+	PLAYER**     allPlayers             = nullptr;
+	int32_t      availableItems[THINGS];
+	SAMPLE*      background_music       = nullptr;
+	char**       bitmap_filenames       = nullptr;
+	int32_t      boxedMode              = BM_OFF;
+	BITMAP**     button                 = nullptr;
+	bool         campaign_mode          = false;
+	double       campaign_rounds        = 0.;   // 20% of the total round number
+	bool         check_for_updates      = true;
+	int32_t      colourDepth            = 0;
+	int32_t      colourTheme            = CT_CRISPY;  // land and sky gradiant theme
+	char         configDir[PATH_MAX + 1];
+	int32_t      current_wallType       = 0;
+	int32_t      custom_background      = 0;
+	char         dataDir[PATH_MAX + 1];
+	int32_t      debris_level           = 1;
+	bool         detailedLandscape      = false;
+	bool         detailedSky            = false;
+	bool         ditherGradients        = true;
+	bool         divide_money           = false;
+	bool         do_box_wrap            = false;
+	bool         drawBackground         = true;
+	bool         dynamicMenuBg          = true;
+	bool         fadingText             = false;
+	int32_t      falling_dirt_balls     = 0;
+	int32_t      fog                    = 0;
+	int32_t      fontHeight             = 0;  // Fixed in ctor, no calls to text_height(font) needed.
+	double       FPS_mod                = 0.; // Pre-calculated, used in many places.
+	int32_t      frames_per_second      = 0;
+	int32_t      full_screen            = FULL_SCREEN_FALSE;
+	char         game_name[GAMENAMELEN + 1];
+	sGfxData     gfxData;
+	double       gravity                = 0.15;
+	int32_t      halfHeight             = DEFAULT_SCREEN_HEIGHT / 2;
+	int32_t      halfWidth              = DEFAULT_SCREEN_WIDTH / 2;
+	double       interest               = 1.25;
+	int32_t      itemtechLevel          = 5;
+	bool         isBoxed                = false;
+	bool         isGameLoaded           = true;
+	int32_t      landSlideDelay         = MAX_GRAVITY_DELAY;
+	int32_t      landSlideType          = SLIDE_GRAVITY;
+	int32_t      landType               = LAND_RANDOM;
+	eLanguages   language               = EL_ENGLISH;
+	int32_t      lightning              = 0;
+	bool         loadGame               = false;
+	FONT*        main_font              = nullptr;
+	int32_t      maxFireTime            = 0;
+	int32_t      maxNumTanks            = 0;
+	double       maxVelocity            = 0.;
+	int32_t      max_screen_updates     = 64;
+	int32_t      menuBeginY             = 0;
+	int32_t      menuEndY               = 0;
+	int32_t      meteors                = 0;
+	BITMAP**     misc                   = nullptr;
+	BITMAP**     missile                = nullptr;
+	int32_t      mouseclock             = 0;
+	bool         nameAboveTank          = true;
+	bool         network_enabled        = false;
+	int32_t      network_port           = DEFAULT_NETWORK_PORT;
+	double       nextCampaignRound      = 0; // When AI players will be raised next
+	int32_t      numAvailable           = 0;
+	int32_t      numGamePlayers         = 0;
+	int32_t      numHumanPlayers        = 0;
+	int32_t      numPermanentPlayers    = 0;
+	int32_t      number_of_bitmaps      = 0;
+	bool         osMouse                = true; // whether we should use the OS or custom mouse
+	bool         play_music             = true;
+	PLAYER**     players                = nullptr;
+	PLAYER*      playerOrder[MAXPLAYERS];
+	uint32_t     rounds                 = 5;
+	int32_t      satellite              = 0;
+	uint32_t     saved_gameindex        = 0;
+	const char** saved_game_list        = nullptr;
+	uint32_t     saved_game_list_size   = 0;
+	int32_t      scoreHitUnit           = 75;
+	int32_t      scoreRoundWinBonus     = 10000;
+	int32_t      scoreSelfHit           = 25;
+	int32_t      scoreTeamHit           = 10;
+	int32_t      scoreUnitDestroyBonus  = 5000;
+	int32_t      scoreUnitSelfDestroy   = 2500;
+	int32_t      screenHeight           = DEFAULT_SCREEN_HEIGHT;
+	int32_t      screenWidth            = DEFAULT_SCREEN_WIDTH;
+	double       sellpercent            = 0.80;
+	char         server_name[129];
+	char         server_port[129];
+	bool         shadowedText           = true;
+	bool         showAIFeedback         = true;
+	bool         showFPS                = false;
+	int32_t      skipComputerPlay       = SKIP_HUMANS_DEAD;
+	BITMAP*      sky                    = nullptr;
+	double       slope[360][2];
+	int32_t      sound_driver           = SD_AUTODETECT;
+	bool         sound_enabled          = true;
+	SAMPLE**     sounds                 = nullptr;
+	int32_t      startmoney             = 15000;
+	BITMAP**     stock                  = nullptr;
+	bool         swayingText            = true;
+	BITMAP**     tank                   = nullptr;
+	BITMAP**     tankgun                = nullptr;
+	int32_t      temp_screenHeight      = 0; // 0 to detect command line arguments
+	int32_t      temp_screenWidth       = 0; // versus loaded configuration.
+	int32_t      time_to_fall           = 0; // amount of time dirt will hover
+	BITMAP**     title                  = nullptr;
+	int32_t      turntype               = TURN_RANDOM;
+	double       viscosity              = 0.5;
+	int32_t      violent_death          = 0;
+	int32_t      voices                 = 0;
+	int32_t      volley_delay           = 10; // Delay factor for volley shots, 5-50
+	int32_t      volume_factor          = MAX_VOLUME_FACTOR;
+	int32_t      wallColour             = GREEN;
+	int32_t      wallType               = WALL_RUBBER;
+	int32_t      weapontechLevel        = 5;
+	BOX          window;
+	int32_t      windstrength           = 8;
+	int32_t      windvariation          = 1;
+
+	// Text structures holding (translated) lines of in game text
+	TEXTBLOCK* gloat        = nullptr;
+	TEXTBLOCK* ingame       = nullptr;
+	TEXTBLOCK* instructions = nullptr;
+	TEXTBLOCK* panic        = nullptr;
+	TEXTBLOCK* kamikaze     = nullptr;
+	TEXTBLOCK* retaliation  = nullptr;
+	TEXTBLOCK* revenge      = nullptr;
+	TEXTBLOCK* suicide      = nullptr;
+	TEXTBLOCK* war_quotes   = nullptr;
+
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	DIR* music_dir = nullptr;
+};
+
+
+#define HAS_ENVIRONMENT 1
 
 #endif
 
diff --git a/src/explosion.cpp b/src/explosion.cpp
index 29b02e9..ed44eda 100644
--- a/src/explosion.cpp
+++ b/src/explosion.cpp
@@ -15,7 +15,11 @@
  * 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.
- * */
+ */
+
+#include "main.h"
+
+#include <cassert>
 
 #include "environment.h"
 #include "globaldata.h"
@@ -26,414 +30,949 @@
 #include "player.h"
 
 
-EXPLOSION::~EXPLOSION ()
+/// @brief constructor for all detonations that are not caused by BEAMs
+EXPLOSION::EXPLOSION (PLAYER* player_, double x_, double y_,
+                      double xv_, double yv_, int32_t type, bool is_weapon) :
+	PHYSICAL_OBJECT(is_weapon),
+	impact_xv(xv_),
+	impact_yv(yv_)
 {
-  /* the immediate destruction of the background creates a need for a second dirt slide,
-    or there are holes left
-  	(important for chain missiles to work properly!) */
-  for (int z = 0; z <= (radius + 2); z++)
-    {
-      setSlideColumnDimensions (_global, _env, x + z, true);
-      setSlideColumnDimensions (_global, _env, x - z, true);
-    }
-  _env->removeObject (this);
-  weap    = NULL;
-  _global = NULL;
-  _env    = NULL;
+	xv = xv_;
+	yv = yv_;
+
+	if ( (TREMOR <= type) && (TECTONIC >= type) )
+		angle = GET_ANGLE(xv, yv);
+	else
+		angle = GET_SAFE_ANGLE(xv, yv, 0);
+
+	player = player_;
+	drag = 0.95;
+	mass = 3;
+	x    = x_;
+	y    = y_;
+	maxVel = env.maxVelocity * (1.20 + (mass / (.01 * MAX_POWER)));
+
+	WEAPON* weap = nullptr;
+	weapType = type;
+	if (weapType < WEAPONS)
+		weap = &weapon[weapType];
+	else
+		weap = &naturals[weapType - WEAPONS];
+
+	radius  = weap->radius;
+	etime   = weap->etime;
+	damage  = weap->damage;
+
+	if ( ( (SHAPED_CHARGE <= weapType) && (CUTTER >= weapType) )
+	  || ( DRILLER == weapType) ) {
+	  	// Uses FlameFront
+		explo_w  = FLAME_W;
+		explo_h  = FLAME_H;
+		centre_x = FLAME_CX;
+		centre_y = FLAME_CY;
+		flame_w  = static_cast<float>(radius) *  2.f;
+		flame_h  = static_cast<float>(radius) / 10.f;
+	} else {
+		// explo_* and centre_* are already set
+		flame_w  = static_cast<float>(radius) * 2.f;
+		flame_h  = static_cast<float>(radius) * 2.f;
+	}
+	scale    = static_cast<float>(radius) / centre_x;
+
+	// make sure dirt appears on the screen, not above the playing area,
+	// and all other explosions at least reach into the area:
+	int32_t minHeightMiss = MENUHEIGHT + (env.isBoxed ? 1 : 0);
+	int32_t minHeightDirt = minHeightMiss + radius;
+	if ( (weapType >= DIRT_BALL)
+	  && (weapType <= SMALL_DIRT_SPREAD) ) {
+		if (y < minHeightDirt)
+			y = minHeightDirt;
+	} else if ( (y < minHeightMiss)
+			&& ( !env.isBoxed
+			  || !env.do_box_wrap
+			  || (WALL_WRAP != env.current_wallType) ) )
+		y = minHeightMiss;
+
+	// For all others
+	// Some weapons have no damage to apply:
+	// Note: Napalm Jellies deal damage over time and not at once.
+	if ( (NAPALM_JELLY == weapType)
+	  || ( (weapType >= RIOT_BOMB) && (weapType <= CLUSTER_MIRV) ) ) {
+		apply_damage = false;
+		// And those wouldn't throw debris or clear terrain either
+		hasThrown  = true;
+		hasCleared = true;
+	} else if (env.debris_level > 0) {
+		maxDebris = (radius * 2) / (8 - (2 * env.debris_level));
+		maxFrame  = 2 + (2 * env.debris_level);
+
+		// Tremor, Shockwave and Tectonic Shift must not throw debris around
+		if ( (TREMOR <= weapType) && (TECTONIC >= weapType))
+			maxDebris = 0;
+		else if (maxDebris < 5)
+			maxDebris = 5;
+	}
+
+	// Lasers and beams do only issue a tiny explosion to add the
+	// dirt throw effect and some boom bang.
+    if ( ( (weapType >= SML_LAZER)     && (weapType <= LRG_LAZER) )
+	  || ( (weapType >= SML_LIGHTNING) && (weapType <= LRG_LIGHTNING) ) ) {
+		apply_damage = false;
+		etime        = 0;
+
+		if (radius < 2)
+			radius = 2;
+
+		maxDebris   /= radius;
+
+		if (maxDebris < 3)
+			maxDebris = 3;
+		else if (maxDebris > 6)
+			maxDebris = 6;
+	}
+
+	// Napalm Jellies need a bit more variation:
+	if (NAPALM_JELLY == weapType)
+		curFrame = ( (rand() % (2 * env.frames_per_second))
+		         - env.frames_per_second) / etime;
+
+	// Unless this is a napalm jelly, that does not clear away any dirt,
+	// lock our field of devastation so no sliding into the explosion occurs
+	else
+		global.addLandSlide(x - radius - 1, x + radius + 1, true);
+
+	// Add to the chain:
+	global.addObject(this);
 }
 
-EXPLOSION::EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos,
-                      int weaponType):PHYSICAL_OBJECT(),weap(NULL)
+
+/// This one is just for the beam and lightning dirt mills.
+EXPLOSION::EXPLOSION (PLAYER* player_, double x_, double y_,
+                      double xv_, double yv_, int32_t type, double damage_,
+                      bool is_weapon) :
+	// delegate base settings
+	EXPLOSION(player_, x_, y_, xv_, yv_, type, is_weapon)
 {
-  drag = 0.95;
-  mass = 3;
-  a = 1;
-  peaked = 0;
-  exclock = 500;
-  bIsWeaponExplosion = true; // Exploding tanks set them to false themselves!
-  setEnvironment (env);
-  player = NULL;
-  _align = LEFT;
-  _global = global;
-  #ifdef NETWORK
-  /*
-  char buffer[256];
-  sprintf(buffer, "EXPLOSION %d %d %d", (int) xpos, (int) ypos, weaponType);
-  global->Send_To_Clients(buffer);
-  */
-  #endif
-  x = xpos;
-  y = ypos;
-  type = weaponType;
-  if (type < WEAPONS)
-    weap = &weapon[type];
-  else
-    weap = &naturals[type - WEAPONS];
-  radius = weap->radius;
-  etime = weap->etime;
-  damage = weap->damage;
-  eframes = weap->eframes;
-
-  // make sure dirt appears on the screen, not above the playing area
-  if ((type >= DIRT_BALL) && (type <= SMALL_DIRT_SPREAD))
-    {
-      if (y < DIRT_CEILING)
-        y = DIRT_CEILING + radius;
-    }
+	damage = damage_;
+}
+
+
+/// @brief default dtor
+EXPLOSION::~EXPLOSION ()
+{
+	// If this is a tremor, the land slide has to be released
+	if ( (TREMOR <= weapType) && (TECTONIC >= weapType))
+		global.unlockLandSlide(dim_cur.x, dim_cur.x + dim_cur.w);
+
+	// Take out of the chain:
+	global.removeObject(this);
 }
 
-EXPLOSION::EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, double xvel, double yvel,
-                      int weaponType):PHYSICAL_OBJECT(),weap(NULL)
+
+/// @brief Physics for the Napalm Jelly, the only explosion that can 'move'
+void EXPLOSION::applyPhysics ()
 {
-  initialise ();
-  setEnvironment (env);
-  _align = LEFT;
-  _global = global;
-  x = xpos;
-  y = ypos;
-  xv = xvel;
-  yv = yvel;
-  angle = (int)(atan2(xv, yv) / PI * 180);
-  while (angle < 0)
-    angle += 360;
-  while (angle > 360)
-    angle -= 360;
-
-  type = weaponType;
-  if (type < WEAPONS)
-    weap = &weapon[type];
-  else
-    weap = &naturals[type - WEAPONS];
-  radius = weap->radius;
-  etime = weap->etime;
-  damage = weap->damage;
-  eframes = weap->eframes;
+	if (NAPALM_JELLY == weapType) {
+		if ( !global.skippingComputerPlay
+		  && !(rand() % (env.frames_per_second / 2)) ) {
+			try {
+				new DECOR (x, y, 0, -2. * env.gravity * env.FPS_mod,
+							radius / 2, DECOR_SMOKE, 0);
+			} catch (std::exception) { /* No reason to fuss, its just smoke. */ }
+		}
+
+		// Instead of the radius, the current blob size is used
+		// for hit and damage calculation. Thus a tank that is only
+		// grazed by the full blob receives a lot less damage.
+		double blobSize = radius - (
+							  static_cast<double>(curFrame)
+							/ static_cast<double>(EXPLOSIONFRAMES)
+							* radius) + 1.;
+		if ( blobSize < 1.)
+			blobSize = 1.;
+
+		// Stop all movement if dirt is hit:
+		bool can_move = (y < env.screenHeight);
+
+		if (can_move && (y < (env.screenHeight - 1))) {
+			if (PINK != getpixel (global.terrain, x, y + 1))
+				can_move = false;
+		}
+
+		if (can_move) {
+			// If the dirt below is falling away, napalm can fall, too:
+			PHYSICAL_OBJECT::applyPhysics();
+
+			// And falling napalm can be repulsed
+			TANK*  lt     = nullptr;
+			double xaccel = 0;
+			double yaccel = 0;
+
+			global.getHeadOfClass(CLASS_TANK, &lt);
+
+			while (lt) {
+				if (!lt->destroy) {
+
+					if (lt->repulse (x + xv, y + yv, &xaccel, &yaccel, physType)) {
+						xv += xaccel;
+						yv += yaccel;
+					}
+				}
+				lt->getNext(&lt);
+			}
+		} else {
+			xv = 0.;
+			yv = 0.;
+		} // End of normal physics
+
+		// Enable next round checking, because dirt can fall
+		// away so the jelly might follow.
+		hitSomething = false;
+
+		// Napalm keeps burning, check all tanks
+		double in_rate_x  = 0.;
+		double in_rate_y  = 0.;
+		TANK*  lt         = nullptr;
+		double damage_mod = blobSize / static_cast<double>(radius);
+
+		global.getHeadOfClass(CLASS_TANK, &lt);
+
+		while (lt) {
+
+			if ( !lt->destroy
+			  && lt->isInEllipse(x, y, blobSize, blobSize, in_rate_x, in_rate_y) ) {
+				double full_rate = in_rate_x * in_rate_y;
+				// If the tank is hit enough for the Napalm to "attach",
+				// stop all movement:
+				if ( (full_rate > 0.9) && (y >= lt->y) ) {
+					xv = 0.;
+					yv = 0.;
+					hitSomething = true;
+					// Note: When the blob shrinks, it can "fall off".
+				}
+
+				// Napalm is *HOT*. Never do less than 50% damage
+				if (full_rate < 0.5)
+					full_rate = 0.5;
+				// Apply damage, but do it per frame
+				lt->addDamage(player,
+							  (  static_cast<double>(damage)
+								* damage_mod * full_rate
+								* (player ? player->damageMultiplier : 1.) )
+						/ static_cast<double>(env.frames_per_second) );
+			}
+
+			lt->getNext(&lt);
+		} // End of looping tanks
+	} // End of NAPALM_JELLY special physics
 }
 
-void EXPLOSION::initialise ()
+
+/// @brief draw the explosions according to weapon type and shape
+void EXPLOSION::draw()
 {
-  PHYSICAL_OBJECT::initialise ();
-  drag = 0.95;
-  mass = 3;
-  a = 1;
-  peaked = 0;
-  exclock = 500;
-	bIsWeaponExplosion = true; // Exploding tanks set them to false themselves!
+	if ( (curFrame > 1) && (curFrame <= (EXPLOSIONFRAMES + 1))
+	  && (NAPALM_JELLY != weapType) ) {
+		/* This group includes:
+		 * - all regular explosives,
+		 * - all items and naturals,
+		 * - tremor, shockwave and tectonic shift.
+		 */
+		int32_t flameIdx = curFrame - 2;
+		int32_t rad      = (radius * curFrame) / EXPLODEFRAMES;
+
+		switch (weapType) {
+			case SHAPED_CHARGE:
+			case WIDE_BOY:
+			case CUTTER:
+				rotate_scaled_sprite (global.canvas, env.gfxData.flameFront[flameIdx],
+									x - radius,
+									y - (radius / 20),
+									itofix (0),
+									ftofix (static_cast<double>(radius) / 300.) );
+
+				setUpdateArea (x - radius - 1, y - (radius / 20) - 1,
+							(radius + 1) * 2, ((radius / 20) + 1) * 2);
+				break;
+			case DRILLER:
+				rotate_scaled_sprite(global.canvas, env.gfxData.flameFront[flameIdx],
+									x - radius,
+									y - (radius / 20),
+									itofix(192),
+									ftofix(static_cast<double>(radius) / 300.) );
+				setUpdateArea(x - (radius / 20) - 1, y - radius - 1,
+							((radius / 20) + 1) * 2, (radius + 1) * 2);
+				break;
+			case TREMOR:
+			case SHOCKWAVE:
+			case TECTONIC:
+				if (curFrame <= (EXPLODEFRAMES + 1) ) {
+					double tst_width = static_cast<double>(curFrame)
+					                 / static_cast<double>(EXPLODEFRAMES)
+					                 * static_cast<double>(radius) / 3.;
+					drawFracture (x, y, angle,
+					              static_cast<int32_t>(.75 + tst_width),
+					              radius * 1.75,
+					              (weapType - TREMOR + 1) * 3, 0);
+					global.addLandSlide(dim_cur.x, dim_cur.x + dim_cur.w, true);
+				}
+				break;
+			case RIOT_BOMB:
+			case HVY_RIOT_BOMB:
+				if (curFrame <= EXPLODEFRAMES) {
+					int32_t colour = player ? player->color : BLUE;
+
+					circlefill (global.terrain, x, y, rad, PINK);
+					circle (global.canvas, x, y, rad, colour);
+
+					setUpdateArea(x - radius - 1, y - radius - 1,
+								(radius + 1) * 2, (radius + 1) * 2);
+				} else if (!peaked) {
+					// Do it here or the slide has an ugly delay.
+					peaked  = true;
+					do_clear();
+				}
+				break;
+			case RIOT_CHARGE:
+			case RIOT_BLAST:
+				if (curFrame <= EXPLODEFRAMES) {
+					double  sx  = x  - env.slope[angle][0] * 15;
+					double  sy  = y  - env.slope[angle][1] * 15;
+					int32_t x1  = sx + env.slope[(angle +  45) % 360][0] * rad;
+					int32_t y1  = sy + env.slope[(angle +  45) % 360][1] * rad;
+					int32_t x2  = sx + env.slope[(angle + 315) % 360][0] * rad;
+					int32_t y2  = sy + env.slope[(angle + 315) % 360][1] * rad;
+
+					triangle (global.canvas, sx, sy, x1, y1, x2, y2, player ? player->color : BLUE);
+					triangle (global.terrain, sx, sy, x1, y1, x2, y2, PINK);
+
+					setUpdateArea (sx - rad - 1, sy - rad - 1,
+								(rad + 1) * 2, (rad + 1) * 2);
+					global.addLandSlide(sx - rad - 1, sx + rad + 1, true);
+				} else if (!peaked) {
+					// Do it here or the slide has an ugly delay.
+					peaked  = true;
+					do_clear();
+				}
+				break;
+			case SML_LAZER:
+			case MED_LAZER:
+			case LRG_LAZER:
+			case SML_LIGHTNING:
+			case MED_LIGHTNING:
+			case LRG_LIGHTNING:
+				setUpdateArea(x - radius - 1, y - radius - 1,
+							(radius + 1) * 2, (radius + 1) * 2);
+				break;
+			case PERCENT_BOMB:
+			default:
+				if ( (weapType <= LAST_EXPLOSIVE)
+				  || (weapType >= WEAPONS)
+				  || (weapType == PERCENT_BOMB) ) {
+					rotate_scaled_sprite(global.canvas, env.gfxData.explosions[flameIdx],
+									x - radius, y - radius,
+									itofix (0),
+									ftofix (static_cast<double>(radius) / 107.) );
+					setUpdateArea(x - radius - 1, y - radius - 1,
+								(radius + 1) * 2, (radius + 1) * 2);
+				} else if (curFrame <= EXPLODEFRAMES) {
+					if ( !peaked
+					  && (weapType >= DIRT_BALL)
+					  && (weapType <= SMALL_DIRT_SPREAD) ) {
+						BITMAP* tmp    = create_bitmap(rad * 2, rad * 2); // for mixing
+						int32_t   colour = player ? player->color : GREEN;
+
+						clear_to_color(tmp, PINK);
+
+						circlefill(tmp, rad, rad, rad - 1, colour);
+
+						// copy terrain over explosion
+						masked_blit(global.terrain, tmp,
+									x - rad, y - rad, 0, 0,
+									rad * 2, rad * 2);
+
+						// blit back exploded terrain
+						masked_blit(tmp, global.terrain,
+									0, 0, x - rad, y - rad,
+									rad * 2, rad * 2);
+						destroy_bitmap(tmp);
+						setUpdateArea (x - rad - 1, y - rad - 1,
+									(rad + 1) * 2, (rad + 1) * 2);
+					} else if (REDUCER == weapType) {
+						int32_t col_front = player ? player->color : SILVER;
+						int32_t col_back  = GetShadeColor(PURPLE, false, col_front);
+
+						int32_t col_mid   = makecol ( (getr(col_front) + getr(col_back)) / 2,
+						                              (getg(col_front) + getg(col_back)) / 2,
+						                              (getb(col_front) + getb(col_back)) / 2);
+						circlefill (global.canvas, x, y, rad, col_front);
+
+						for (int32_t i = 1 + (curFrame % 2); i < rad; i += 2)
+							circle(global.canvas, x, y, i, i < (rad / 2) ? col_mid : col_back);
+						setUpdateArea (x - rad - 1, y - rad - 1,
+									(rad + 1) * 2, (rad + 1) * 2);
+					} else {
+						// This is something else. But what?
+						fprintf(stderr, "EXPLOSION::draw() Unknown weapon type %d\n",
+									weapType);
+						circlefill (global.canvas, x, y, rad, RED);
+						setUpdateArea (x - rad - 1, y - rad - 1,
+									(rad + 1) * 2, (rad + 1) * 2);
+					}
+				} else if (!peaked && (curFrame > EXPLODEFRAMES)
+						&& (DIRT_BALL <= weapType)
+						&& (SUP_DIRT_BALL >= weapType) ) {
+					// Do it here or the slide has an ugly delay.
+					peaked  = true;
+					do_clear();
+				}
+				break;
+		}
+	} else if (NAPALM_JELLY == weapType) {
+		// The Jelly does not use flameFront and is drawn individually.
+		int32_t blobSize = curFrame > 0
+		                 ? static_cast<int32_t>(radius
+								- (   static_cast<double>(curFrame)
+									/ static_cast<double>(EXPLOSIONFRAMES)
+									* radius)) + 1
+		                 : radius;
+		if ( (blobSize > 0) && (curFrame <= (EXPLOSIONFRAMES + 1)) ) {
+			if (blobSize < 2)
+				// avoid circle size crash
+				blobSize = 2;
+			draw_Napalm_Blob(this, x, y, blobSize, curFrame);
+		}
+	}
+
+	/// === Allow clearing once peaked on high enough frame ===
+	///---------------------------------------------------------
+	if (!peaked && (curFrame > EXPLODEFRAMES) && (NAPALM_JELLY != weapType)) {
+		peaked  = true;
+		do_clear();
+	}
+
+	/// === Throw if the frame is in range and it has not thrown, yet ===
+	///-------------------------------------------------------------------
+	if ( !hasThrown
+	  && (curFrame > 1)
+	  && (curFrame <= EXPLOSIONFRAMES)
+	  && ( (weapType <= TECTONIC) || (weapType > REDUCER) )
+	  && (NAPALM_JELLY != weapType) ) {
+
+		do_throw();
+
+		// The tremor types do not need clearing!
+		if ( (TREMOR > weapType) || (TECTONIC < weapType) )
+			do_clear();
+	}
 }
 
-int EXPLOSION::applyPhysics ()
+
+/// @brief Draw recursive fractures
+void EXPLOSION::drawFracture(int32_t x, int32_t y, int32_t frac_angle,
+                             int32_t width, int32_t segmentLength,
+                             int32_t maxRecurse, int32_t recurseDepth)
 {
-  if (type == NAPALM_JELLY)
-    {
-      if (rand () % 300 == 0)
-      {
-          DECOR *decor;
-          decor = new DECOR (_global, _env, x, y, xv, yv, radius / 2, DECOR_SMOKE);
-          if (!decor)
-            {
-              perror ( "explosion.cc: Failed allocating memory for decor in applyPhysics");
-              // exit (1);
-            }
-        // If the dirt below is falling away, napalm can fall, too:
-        if (getpixel(_env->terrain, x, y + 1) == PINK)
-        {
-           // yv = _env->gravity * (100.0 / FRAMES_PER_SECOND);
-           yv = _env->gravity * (100.0 / _global->frames_per_second);
-           PHYSICAL_OBJECT::applyPhysics();
-        }
-        else
-          yv = 0.0;
-      }
-    }
-  /*	if (dispersing)
-  	{
-  		double accel = (_env->wind - xv) / mass * drag * _env->viscosity;
-  		xv += accel;
-  		x += xv;
-  		y += yv;
-  	}
-  */
-  return (hitSomething);
+	double xLen = env.slope[frac_angle][1] * width;
+	double yLen = env.slope[frac_angle][0] * width;
+	int32_t x1 = x + xLen;
+	int32_t y1 = y + yLen;
+	int32_t x2 = x - xLen;
+	int32_t y2 = y - yLen;
+	int32_t x3 = x + (env.slope[frac_angle][0] * segmentLength);
+	int32_t y3 = y + (env.slope[frac_angle][1] * segmentLength);
+
+	triangle (global.terrain, x1, y1, x2, y2, x3, y3, PINK);
+
+	if (!recurseDepth) {
+		dim_cur.x = x1;
+		dim_cur.y = y1;
+		dim_cur.w = x1;
+		dim_cur.h = y1;
+	} else {
+		dim_cur.x = std::min(std::min(std::min(x1, x2), x3), dim_cur.x);
+		dim_cur.y = std::min(std::min(std::min(y1, y2), y3), dim_cur.y);
+		dim_cur.w = std::max(std::max(std::max(x1, x2), x3), dim_cur.w);
+		dim_cur.h = std::max(std::max(std::max(y1, y2), y3), dim_cur.h);
+	}
+
+	if (recurseDepth < maxRecurse) {
+		for (int32_t branchCount = 0; branchCount < 3; ++branchCount) {
+			if ( branchCount || (Noise(x + y + branchCount) < 0)) {
+				int32_t reduction = 2;
+				int32_t newAngle  = frac_angle;
+
+				switch(branchCount) {
+				case 1:
+					newAngle += 90 + (Noise (x + y + 25 + branchCount) * 22.5);
+					reduction = ROUNDu(Noise(x + y + 1 + branchCount) * 4) + 3;
+					break;
+				case 2:
+					newAngle += 270 + (Noise (x + y + 32 + branchCount) * 22.5);
+					reduction = ROUNDu(Noise (x + y + 2 + branchCount) * 4) + 3;
+					break;
+				case 0:
+				default:
+					newAngle += Noise(x + y + 4) * 30;
+					break;
+				}
+
+				while (newAngle < 0)
+					newAngle += 360;
+				newAngle %= 360;
+
+				if (reduction < 2)
+					reduction = 2;
+
+				drawFracture (x3, y3, newAngle, width / reduction,
+				              segmentLength / reduction, maxRecurse,
+				              recurseDepth + 1);
+			}
+		}
+	}
+
+	// Calculate width and height, previously right and bottom
+	if (!recurseDepth) {
+		dim_cur.w -= dim_cur.x;
+		dim_cur.h -= dim_cur.y;
+	}
 }
 
 
 void EXPLOSION::explode ()
 {
-  int calcblow;
-  double distance;
-  int z;
-  calcblow = 1;
-
-	/* to allow synchronous tank explosions, explosions need to know what they are */
-	if (player && player->tank && (type < WEAPONS))
-		bIsWeaponExplosion = true;
-	else
-		bIsWeaponExplosion = false;
-
-  if (peaked == 1 && a <= EXPLOSIONFRAMES + 1)
-    {
-      exclock++;
-      if (exclock > weap->etime)
-        {
-          exclock = 0;
-          a++;
-          requireUpdate ();
-        }
-      if (a > EXPLOSIONFRAMES + 1)
-        {
-          destroy = TRUE;
-        }
-    }
-  if (a <= EXPLODEFRAMES + 1)
-    {
-      TANK *tank;
-      if (a == 1)
-        {
-          for (int index = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &index)) && tank; index++)
-            {
-              // is tank directly above explosion?
-              if ((fabs (x - tank->x) < radius) && (y - tank->y >= 0))
-                tank->creditTo = player;
-            }
-        }
-      if ((a == 1) && (type <= TECTONIC || type >= WEAPONS || type == PERCENT_BOMB || type == REDUCER))
-        {
-          for (int index = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &index)) && tank; index++)
-            {
-              if (type >= SHAPED_CHARGE && type <= CUTTER)
-                {
-                  double dXDistance = fabs (x - tank->x);
-                  double dYDistance;
-                  if (y > tank->y)
-                    dYDistance     = fabs (y - tank->y) - (TANKHEIGHT * (3.0 / 4.0)); // To include the bottom of the tank
-                  else
-                    dYDistance     = fabs (y - tank->y) - (TANKHEIGHT * (3.0 / 8.0)); // The top has to be hit, but not only brushed
-
-                  if (dYDistance < 0) dYDistance = 0;
-
-                  if ( (dYDistance  <= (radius / 20))
-                       && (dXDistance  >= (TANKWIDTH / 2)))
-                    distance = ABSDISTANCE(dXDistance, dYDistance, 0, 0);
-                  else
-                    distance = 2 * radius; // clear outside the explosion!
-#ifdef DEBUG
-                  if ((dXDistance < (radius + TANKHEIGHT / 2)) && (dYDistance < 25))
-                    {
-                      cout << endl << "Shape: " << radius << " x " << (radius / 20) << endl;
-                      printf( "Tank  X = % 4d, Tank  Y = % 4d\n", (int)tank->x, (int)tank->y);
-                      printf( "Explo X = % 4d, Explo Y = % 4d\n", (int)x, (int)y);
-                      printf( "Dist  X = % 4d, Dist  Y = % 4d\n", (int)dXDistance, (int)dYDistance);
-                      cout << "Distance: " << distance << endl;
-                    }
-#endif // DEBUG
-                }
-              else
-                distance = ABSDISTANCE(x, y, tank->x, tank->y);
-
-              if (distance <= (radius + TANKHEIGHT/2) && tank->l > 0)
-                {
-                  _global->updateMenu = 1;
-
-                  if (type == PERCENT_BOMB)
-                    tank->damage = (int) ( (tank->l + tank->sh) / 2) + 1;
-                  else if ( type == REDUCER )
-                  {
-                    if (tank->player->damageMultiplier > 0.1)
-                       tank->player->damageMultiplier *= 0.75;
-                  }
-                  else if (player)
-                    tank->damage = (int) ((float) damage * ((float) 1 - ((fabs (distance) / (float)radius) / 2)) * player->damageMultiplier);
-                  // player is not used in weather attacks
-                  else
-                    tank->damage = (int) (float) damage * ((float) 1 - ((fabs (distance) / (float)radius) / 2));
-                  tank->creditTo = player;
-                  tank->applyDamage ();
-                }
-            }
-          if (type >= TREMOR && type <= TECTONIC)
-            {
-              angle = (int)(atan2 (yv, xv) / PI * 180);
-              if (angle < 0)
-                angle = angle + 360;
-              angle = angle % 360;
-            }
-        }
-      exclock++;
-      if (exclock > weap->etime)
-        {
-          exclock = 0;
-          a++;
-          requireUpdate ();
-        }
-      if (a >= EXPLODEFRAMES + 1 && !peaked)
-        {
-          calcblow = 0;
-        }
-    }
-  if (!calcblow)
-    {
-      if (type >= SHAPED_CHARGE && type <= CUTTER)
-        {
-          ellipsefill (_env->terrain, (int)x, (int)y, radius, radius / 20, PINK);
-          setUpdateArea ((int)x - (radius + 1), (int)y - (radius / 20 + 1), (radius + 1) * 2, (radius / 20 + 1) * 2);
-
-        }
-      
-      else if (type == DRILLER)
-      {
-           ellipsefill (_env->terrain, (int) x, (int) y, radius / 20, radius, PINK);
-           setUpdateArea( (int) x - (radius + 1), (int) y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-      }
-      
-      else if (type <= LAST_EXPLOSIVE || type >= WEAPONS || type == PERCENT_BOMB)
-        {
-
-          if (type != NAPALM_JELLY)
-            {
-              circlefill (_env->terrain, (int)x, (int)y, radius, PINK);
-// too much?  setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-// still too much! setUpdateArea ((int)x - radius, (int)y - radius, radius * 2, radius * 2);
-              setUpdateArea ((int)x - radius, (int)y - radius, (radius * 2) - 2, (radius * 2) - 2);
-            }
-        }
-      else  if ((type >= RIOT_BOMB) && (type <= HVY_RIOT_BOMB))
-        {
-          circlefill (_env->terrain, (int)x, (int)y, radius, PINK);
-          setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-        }
-      else if ((type >= RIOT_CHARGE) && (type <= RIOT_BLAST))
-        {
-          double sx = x - _global->slope[angle][0] * 15;
-          double sy = y - _global->slope[angle][1] * 15;
-          triangle (_env->terrain,
-                    (int)sx,
-                    (int)sy,
-                    (int)(sx + _global->slope[(angle + 45) % 360][0] * radius),
-                    (int)(sy + _global->slope[(angle + 45) % 360][1] * radius),
-                    (int)(sx + _global->slope[(angle + 315) % 360][0] * radius),
-                    (int)(sy + _global->slope[(angle + 315) % 360][1] * radius), PINK);
-          setUpdateArea ((int)sx - (radius + 1), (int)sy - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-        }
-      else if ((type >= DIRT_BALL) && (type <= SMALL_DIRT_SPREAD))
-        {
-          BITMAP *tmp;		//for mixing
-
-          tmp = create_bitmap(radius * 2, radius * 2);
-          clear_to_color(tmp, PINK);
-          for (int count = 0; count < radius ; count++)
-            {
-              circle (tmp, radius, radius, count, (player)?player->color: makecol (0, 255, 0) );
-            }
-          setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-
-          //copy terrain over explosion
-          masked_blit(_env->terrain, tmp, (int)x - radius, (int)y - radius, 0, 0, radius*2, radius*2);
-
-          //blit back exploded terrain
-          masked_blit(tmp, _env->terrain, 0, 0,(int)x - radius, (int)y - radius, radius*2, radius*2);
-
-          destroy_bitmap(tmp);
-        }
-
-      for (z = 0; z < (_current.w + 2); z++)
-        setSlideColumnDimensions (_global, _env, _current.x + z, TRUE);
-      calcblow = 1;
-      peaked = 1;
-      dispersing = 1;
-    }
+	/// === Time and frame advancement ===
+	///------------------------------------
+	if (curFrame <= (EXPLOSIONFRAMES + 1) ) {
+		if (++exclock > etime) {
+			exclock = 0;
+			++curFrame;
+			requireUpdate();
+
+		}
+	} // End of time and frame advancement
+
+	// Check whether the explosion ends:
+	if (curFrame > (EXPLOSIONFRAMES + 1))
+		destroy = true;
+
+	/// === Apply Damage if not done, yet ===
+	///---------------------------------------
+	if (apply_damage) {
+		// In this case the affected tanks must be checked first
+		TANK*      lt    = nullptr;
+		weaponType wType = static_cast<weaponType>(weapType);
+
+		// But do not check dirt balls, they deal no damage
+		if ( (DIRT_BALL     > weapType)
+		  || (SUP_DIRT_BALL < weapType) ) {
+
+			global.getHeadOfClass(CLASS_TANK, &lt);
+
+			while (lt) {
+				double dmg = get_hit_damage(lt, wType, x, y);
+
+				if (dmg > 0.) {
+					if (PERCENT_BOMB == weapType)
+						lt->addDamage(player, dmg); // already set, no multiplier
+					else if (REDUCER == weapType) {
+						lt->player->damageMultiplier *= 0.75; // already checked
+					} else
+						lt->addDamage(player, dmg
+									* (player ? player->damageMultiplier : 1.) );
+				} // End of having damage to deal
+
+				lt->getNext(&lt);
+			} // End of looping tanks
+		} // end of having no dirt ball
+		apply_damage = false;
+	} // End of handling damage
+}
+
+
+// ======================================
+// === Private method implementations ===
+// ======================================
+
+/// @brief Clear the background (Display must be locked!)
+void EXPLOSION::do_clear()
+{
+	if (hasCleared && hasSlid)
+		return;
+
+	// Use a calculated radius for pre-mature clearing
+	int32_t rad = (radius * curFrame) / EXPLODEFRAMES;
+
+	// If the radius is below 3, the early clearing would jeopardize
+	// debris throwing, so opt out if the radius is much larger
+	if ( (rad < 3) && (radius > 5) )
+		return;
+
+	// Do not clear/slide more than the real radius
+	if (rad > radius)
+		rad = radius;
+
+	// Raise rad so no flood of "(rad + 1)" calculations is needed.
+	int32_t area_rad = rad + 1;
+
+	if (!hasCleared) {
+
+		// Now clear according to weapon type
+		if ( (weapType >= SHAPED_CHARGE) && (weapType <= CUTTER) ) {
+			int32_t yrad = (rad - 1) / 20;
+			ellipsefill (global.terrain, x, y, rad, yrad, PINK);
+			global.addLandSlide(x - area_rad, x + area_rad, true);
+			addUpdateArea (x - area_rad, y - (yrad + 1),
+						area_rad * 2, (yrad + 1) * 2);
+		} else if (weapType == DRILLER) {
+			int32_t xrad = rad / 20;
+			ellipsefill (global.terrain, x, y, xrad, rad, PINK);
+			global.addLandSlide(x - xrad - 1, x + xrad + 1, true);
+			addUpdateArea (x - (xrad + 1), y - area_rad,
+						(xrad + 1) * 2, area_rad * 2);
+		} else if (( (weapType <= LAST_EXPLOSIVE)
+				  || (weapType >= WEAPONS)
+				  || (weapType == PERCENT_BOMB)
+				  || ( (weapType >= SML_LAZER)     && (weapType <= LRG_LAZER) )
+				  || ( (weapType >= SML_LIGHTNING) && (weapType <= LRG_LIGHTNING) ) )
+				&& (NAPALM_JELLY != weapType) ) {
+			circlefill (global.terrain, x, y, rad, PINK);
+			global.addLandSlide(x - area_rad, x + area_rad, true);
+			addUpdateArea (x - area_rad, y - area_rad,
+						area_rad * 2, area_rad * 2);
+		}
+
+	} // End of clearing
+
+	// If rad did not reach radius (yet), this isn't done
+	if ( (rad >= radius) || peaked) {
+
+		if (!hasSlid) {
+			// Allow the land slide to happen:
+			int32_t area_rad = 1 + ((weapType == DRILLER) ? rad / 20 : rad);
+			global.unlockLandSlide(x - area_rad, x + area_rad);
+			hasSlid = true;
+		}
+
+		if (!hasCleared)
+			hasCleared = true;
+	}
+}
+
+
+/// @brief Throw some debris
+void EXPLOSION::do_throw()
+{
+	// Do never throw when skipping AI play, and
+	// opt out if debris generation is forbidden
+	if (!hasThrown
+	  && (  global.skippingComputerPlay
+		|| (hasDebris >= maxDebris)
+		|| (curFrame  >  maxFrame ) ) )
+		hasThrown = true;
+
+	// Early out if this is already done or no further deco is allowed
+	if (hasThrown || global.hasTooMuchDeco)
+		return;
+
+	// The delay used for smoke and debris.
+	int32_t delay_dirt  = (etime * (maxFrame - curFrame)) - exclock;
+	int32_t delay_smoke = (etime * (EXPLODEFRAMES - curFrame)) - exclock;
+	// Note: DECOR checks against delay>0, thus a negative delay
+	//       is no problem here.
+
+	// The radius is not always the radius, so use shortcuts:
+	int32_t rad  = (radius * maxFrame) / EXPLODEFRAMES;
+	int32_t xrad =    DRILLER       == weapType    ? rad / 20 : rad;
+	int32_t yrad = ( (SHAPED_CHARGE <= weapType)
+				  && (CUTTER        >= weapType) ) ? rad / 20 : rad;
+
+	// A minimum radius of 1 is needed for the debris seek to make sense:
+	if (rad < 1)
+		return;
+
+	// Use limited rounds for debris creation to ensure no
+	// endless loops are created if there is no terrain to throw.
+	int32_t deb_round  = 0;
+	int32_t max_rounds = 3 * env.debris_level;
+
+	// Initial velocity is modified by damage:
+	double damage_mod = 1. + (static_cast<double>(damage)
+							/ static_cast<double>(weapon[DTH_HEAD].damage)
+							/ 2. );
+
+	// If this is a meteor, the damage mod is tweaked or the debris more
+	// looks like a collapsing rock than anything thrown.
+	bool    isMeteor = ((SML_METEOR <= weapType) && (LRG_METEOR >= weapType));
+	BITMAP* meteor   = nullptr;
+	if (isMeteor) {
+		meteor      = env.missile[naturals[weapType - WEAPONS].picpoint];
+		damage_mod *= 2. * static_cast<double>(weapType - SML_METEOR + 1);
+	}
+
+	// Now move through the x-axis and create debris and smoke.
+	double  xpos   = 0;
+	double  ypos   = 0;
+	double  bottom = env.screenHeight;
+	double  alpha  = 0.;
+	double  minX   = x - xrad;
+	double  maxY   = 0.;
+	int32_t round  = 1;
+	int32_t deb_rad, diameter, seek_area = hasDebris % 3;
+	double  max_x_vel, max_y_vel, mod_x_vel, mod_y_vel;
+
+	do {
+		deb_rad   = 1 + (rad > 1 ? (rand() % std::min(5, rad)) : 0);
+		diameter  = 2 * deb_rad;
+		max_x_vel =  16. - (static_cast<double>(deb_rad) * 2.0);
+		max_y_vel = -17. + (static_cast<double>(deb_rad) * 1.5);
+
+		// The mods are lower than the max, as the velocity values
+		// can be modified up by damage and position.
+		mod_x_vel = max_x_vel *  0.66;
+		mod_y_vel = max_y_vel * -0.50; // Make positive
+
+		// If this isn't weapon fire, reduce the maximum velocities
+		if (!isWeaponFire) {
+			max_x_vel *= 0.75;
+			max_y_vel *= 0.66;
+		}
+
+
+		// Were the routine looks for dirt is determined by the
+		// current amount of debris found:
+		xpos = ( seek_area
+				 ? 1 == seek_area
+				   ? minX         // left
+				   : x            // right
+				 : x - (xrad / 2) // centre
+			   ) + (rand() % xrad);
+
+		/* Circle Coordinates:
+		 * X = cos(alpha) * radius (xrad)
+		 * Y = sin(alpha) * radius (yrad)
+		 * alpha is the angle between x and y.
+		 * cos(alpha) is x / xrad
+		 * sin(alpha) is y / yrad
+		 * So if we have alpha, we can produce Y.
+		 */
+		alpha = std::acos((xpos - x) / xrad);
+		// Note: This results in radians, but that is okay,
+		//       as sin() needs radians anyway.
+		ypos  = y - ROUND(std::sin(alpha) * yrad);
+		maxY  = std::min(y + (y - ypos), bottom - deb_rad);
+
+        // find first earth pixel:
+        if ( (ypos < y) && (maxY > ypos)
+		  && checkPixelsBetweenTwoPoints(&xpos, &ypos, xpos, maxY)) {
+
+			// Try to get a free debris pool item
+			sDebrisItem* deb_item = global.get_debris_item(deb_rad);
+
+			if (nullptr == deb_item) {
+				// pool is full and at its limit.
+				hasDebris = maxDebris;
+				break;
+			}
+
+			// Try to get another free item if this is a meteor
+			sDebrisItem* met_item = isMeteor
+			                      ? global.get_debris_item(deb_rad)
+			                      : nullptr;
+			// Note: It is not a problem if a meteor got no met_item.
+
+			// Move down a bit...
+			ypos += 1 + (rand() % deb_rad);
+			// ... but do not end up below maxY
+			if (ypos > maxY)
+				ypos = maxY;
+
+
+			// Extract the pixels around xpos/ypos
+			int32_t left = xpos - deb_rad;
+			int32_t top  = ypos - deb_rad;
+
+			// Blit in terrain
+			blit(global.terrain, deb_item->bmp, left, top,
+			     0, 0, diameter + 1, diameter + 1);
+
+			// Blit in meteor if needed
+			if (isMeteor && meteor && met_item)
+				blit(meteor, met_item->bmp,
+						left % ( meteor->w - diameter),
+						top  % (meteor->h - diameter),
+						0, 0, diameter + 1, diameter + 1);
+
+			// Now the distance from the lowest centre point can be used
+			// to determine the initial velocity of the debris.
+			double dyp = y + radius + diameter;
+			double dxv = ((xpos - x  ) / xrad  ) * mod_x_vel * damage_mod;
+			double dyv = ((ypos - dyp) / radius) * mod_y_vel * damage_mod;
+
+			assert((dyv < 0.) && "Check it: Dirt shall be thrown down?");
+
+			// Modify x and y velocity a bit randomly for more variance
+			dxv *= 1. + (static_cast<double>((rand() % 9) - 4) / 10.);
+			dyv *= 1. + (static_cast<double>((rand() % 7) - 3) / 10.);
+
+			// Apply impact velocity
+			dxv -= impact_xv / ( (std::abs(dxv) * .75) + 1.5);
+			dyv -= impact_yv / ( (std::abs(dyv) * .75) + 1.5);
+
+			// Maximum x and y velocity depends on the radius of the debris:
+			if (std::abs(dxv) > max_x_vel)
+				dxv = SIGNd(dxv) * max_x_vel;
+			if (dyv < max_y_vel)
+				dyv = max_y_vel;
+
+			// Move the decoration out to the rim of the final explosion:
+			double rimx = x + ROUND(std::cos(alpha) * xrad);
+			double rimy = y - ROUND(std::sin(alpha) * yrad);
+
+			try {
+				new DECOR(rimx, rimy, dxv, dyv,
+						  deb_rad, DECOR_DIRT, delay_dirt, deb_item, met_item);
+				// Clear the source to not take these pixels again.
+				circlefill (global.terrain, xpos, ypos, deb_rad, PINK);
+				addUpdateArea(xpos - deb_rad, ypos - deb_rad, diameter, diameter);
+				addUpdateArea(rimx - deb_rad, rimy - deb_rad, diameter, diameter);
+			} catch (...) {
+				// As the decor was not created, the item can be released again
+				global.free_debris_item(deb_item);
+				if (met_item)
+					global.free_debris_item(met_item);
+			}
+
+			// Every throw needs a smoke... ;)
+			try {
+				new DECOR(xpos, ypos, dxv, dyv,
+						  deb_rad * 3, DECOR_SMOKE, delay_smoke);
+			} catch (...) { /* nothing.. really... it doesn't matter */ }
+
+			// Advance hasDebris and get new seeking area
+			seek_area = ++hasDebris % 3;
+        } // End of having hit a pixel
+
+        // Count tries to advance rounds (if needed)
+		if (++deb_round >= maxDebris) {
+			deb_round = 0;
+			++round;
+		}
+	} while ( (round < max_rounds) && (hasDebris < maxDebris) );
+
+	// If the calculated radius did not reach radius (yet), and hasDebris
+	// has not reached maxDebris, yet, the throwing is not finished, yet.
+	if ( (rad >= radius) || (hasDebris >= maxDebris))
+		hasThrown = true;
+
 }
 
-void EXPLOSION::draw (BITMAP *dest)
+
+// =======================
+// === Global helpers: ===
+// =======================
+
+
+/// @brief draw one blob of Napalm (display be locked!)
+void draw_Napalm_Blob(VIRTUAL_OBJECT* blob, int32_t x, int32_t y,
+					int32_t radius, int32_t frame)
 {
-  if (type >= SHAPED_CHARGE && type <= CUTTER)
-    {
-      if (a > 1 && a <= EXPLOSIONFRAMES + 1)
-        {
-          rotate_scaled_sprite (dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2],
-                                (int)x - radius, (int)y - (radius / 20), itofix (0), ftofix ((double) radius / 300));
-          setUpdateArea ((int)x - (radius + 1), (int)y - (radius / 20 + 1), (radius + 1) * 2, (radius / 20 + 1) * 2);
-        }
-    }
-  else if (type == DRILLER)
-  {
-     if (a > 1 && a <= EXPLOSIONFRAMES + 1)
-     {
-        rotate_scaled_sprite(dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2], (int) x - (radius), (int) y - (radius / 20), itofix(192), ftofix(radius / 300));
-        // rotate_scaled_sprite (dest, (BITMAP *) _global->gfxData.flameFront[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2], (int)x - radius, (int)y - (radius / 20), itofix (0), ftofix ((double) radius / 300));
-
-        setUpdateArea( (int) x - (radius + 1), (int) y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-     }
-  }
-  else if (type == NAPALM_JELLY)
-    {
-      int blobSize = (int)(radius - ((double)a / EXPLOSIONFRAMES) * radius + 1);
-      if (blobSize < 2) blobSize = 2;       // avoid circle size crash
-      circlefill (_env->db, (int)x, (int)y, blobSize - 2, makecol (255, 0, 0));
-      circle(_env->db, (int)x, (int)y, blobSize - 1, makecol(255, 150, 0));
-      circle(_env->db, (int)x, (int)y, blobSize, makecol(255, 150, 0));
-      setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-    }
-  else if (type <= LAST_EXPLOSIVE || type >= WEAPONS || type == PERCENT_BOMB)
-    {
-      if (a > 1 && a <= EXPLOSIONFRAMES + 1)
-        {
-          /*  - background needs to be cleard immediately to allow chain missiles to work
-          		  (and horizontal spreads look *far* better, too! ;) )
-          		Note: Shaped charges are always shot alone, and they live off their visual effect.
-          					Adding the immediate destruction on them, too, would cut off some of the effect... */
-          if	(a <= EXPLODEFRAMES)
-            circlefill (_env->terrain, (int)x, (int)y, (int)((radius / EXPLODEFRAMES) * a), PINK);
-          rotate_scaled_sprite(dest, (BITMAP *) _global->gfxData.explosions[(a + (EXPLOSIONFRAMES * weap->eframes)) - 2],
-                               (int)x - radius, (int)y - radius, itofix (0), ftofix ((double) radius / 107));
-          setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-        }
-    }
-  else if ( (type <= TECTONIC) && (type >= TREMOR) )
-    {
-      if (a > 1 && a <= EXPLODEFRAMES + 1)
-        {
-          drawFracture (_global, _env, _env->terrain, &_current, (int)x, (int)y, angle, (int)(((double)a / EXPLODEFRAMES) * (radius / 4)), radius, 5, 0);
-        }
-    }
-  else if ((type >= RIOT_BOMB) && (type <= HVY_RIOT_BOMB))
-    {
-      if (a > 1 && a <= EXPLODEFRAMES + 1)
-        {
-          int startCirc = (radius / EXPLODEFRAMES) * a;
-          int colour = (player) ? player->color : makecol(0, 0, 255);
-          circlefill (_env->terrain, (int)x, (int)y, startCirc, PINK);
-          circle (dest, (int)x, (int)y, startCirc, colour);
-          setUpdateArea ((int)x - (radius + 1), (int)y - (radius + 1), (radius + 1) * 2, (radius + 1) * 2);
-        }
-    }
-  else if ((type >= RIOT_CHARGE) && (type <= RIOT_BLAST))
-    {
-      if (a > 1 && a <= EXPLODEFRAMES + 1)
-        {
-          double sx = x - _global->slope[angle][0] * 15;
-          double sy = y - _global->slope[angle][1] * 15;
-          int startCirc = (radius / EXPLODEFRAMES) * a;
-          triangle (dest, (int)sx, (int)sy, (int)(sx + _global->slope[(angle + 45) % 360][0] * startCirc), (int)(sy + _global->slope[(angle + 45) % 360][1] * startCirc),(int)(sx + _global->slope[(angle + 315) % 360][0] * startCirc),(int)(sy + _global->slope[(angle + 315) % 360][1] * startCirc), player->color);
-          setUpdateArea ((int)sx - (startCirc + 1), (int)sy - (startCirc + 1), (startCirc + 1) * 2, (startCirc + 1) * 2);
-        }
-    }
-  else
-    {
-      if (a > 1 && a <= EXPLODEFRAMES + 1)
-        {
-          int startCirc = (radius / EXPLODEFRAMES) * a;
-          circlefill (dest, (int)x, (int)y, startCirc, (player)?player->color: makecol (0, 255, 0) );
-          startCirc += (radius / EXPLODEFRAMES) * 2;
-          setUpdateArea ((int)x - startCirc, (int)y - startCirc, startCirc * 2, startCirc * 2);
-        }
-    }
+	if (nullptr == blob)
+		return;
+
+	int32_t phase = std::abs(frame) % 4;
+	int32_t lo_mod = phase % 2;
+	int32_t hi_mod = (phase + 1) % 2;
+
+	circlefill (global.canvas, x, y, radius, RED);
+
+	for (int32_t i = 1; i < radius; i += 3) {
+		int32_t r = 255 - (20 * phase);
+		int32_t g =        25 * phase;
+		int32_t b =        10 * phase;
+		circle(global.canvas, x, y, i + lo_mod, makecol(r,      g, 10 + b));
+		circle(global.canvas, x, y, i + hi_mod, makecol(r, 25 + g,      b));
+	}
+
+	blob->setUpdateArea (x - radius - 1, y - radius - 1, (radius + 1) * 2, (radius + 1) * 2);
+	blob->requireUpdate ();
 }
 
-int EXPLOSION::isSubClass (int classNum)
+
+/** @brief return the damage of a weapon against a tank
+  * @param[in] tank Pointer to the tank to check
+  * @param[in] type The weapon used
+  * @param[in] hit_x X coordinate of the impact
+  * @param[in] hit_y Y coordinate of the impact
+  * @return The part damage of the weapon without player modification
+**/
+double get_hit_damage(TANK* tank, weaponType type, int32_t hit_x, int32_t hit_y)
 {
-  if (classNum == EXPLOSION_CLASS)
-    return (TRUE);
-  else
-    return (FALSE);
-  //return (PHYSICAL_OBJECT::isSubClass (classNum));
+	if ( (nullptr == tank) || (tank->destroy) )
+		return 0.;
+
+	double weap_rad  = weapon[type].radius;
+	double xrad      =    DRILLER       == type    ? weap_rad / 20. : weap_rad;
+	double yrad      = ( (SHAPED_CHARGE <= type)
+	                  && (CUTTER        >= type) ) ? weap_rad / 20. : weap_rad;
+	double in_rate_x = 0.;
+	double in_rate_y = 0.;
+	double dmg       = 0.;
+
+	if (tank->isInEllipse(hit_x, hit_y, xrad, yrad, in_rate_x, in_rate_y) ) {
+		if (PERCENT_BOMB == type)
+			dmg = ((tank->l + tank->sh) / 2) + 1;
+		else if ( (REDUCER == type)
+		       && (tank->player->damageMultiplier > 0.1) )
+			dmg = 1.; // So result > 0 can be checked
+
+		// Shaped charges and drillers have a minimum distance under which they
+		// deal no damage:
+		else if ( ( ( (SHAPED_CHARGE > type) || (CUTTER < type) ) //     ( Not shaped
+		         || (std::abs(tank->x - hit_x) > yrad) )          //       or x distance okay )
+		       && ( (DRILLER != type)                             // and (not driller
+		         || (std::abs(tank->y - hit_y) > xrad) ) ) {      //       or y distance okay )
+			/* Note: The radii are reversed as the opposite radius is the
+			 *       minimum distance needed for the main blast radius.
+			 * Note: The above is built from the following formula:
+			 * Be a = Weapon is a shaped charge, wide boy or cutter,
+			 *    b = x distance is greater than the weapons x range minimum,
+			 *    c = Weapon is the driller,
+			 *    d = y distance is greater than the weapons y range minimum.
+			 * Then the following term must be true to be allowed to enter this block:
+			 * (~a v b) ^ (~c v d) which reads like:
+			 *       (Not a shaped weapon OR the x distance is in order)
+			 *   AND (Not the driller     OR the y distance is in order)
+			 * The above if block actually is exactly that from bottom to top.
+			 */
+
+			dmg = weapon[type].damage;
+
+			// Some weapons have minimum rates on axis ratings
+			if (DRILLER == type) {
+				// The driller has its force focused vertically:
+				if (in_rate_y < 0.95)
+					in_rate_y = 0.95;
+			} else if ( (SHAPED_CHARGE <= type)
+			         && (CUTTER        >= type) ) {
+				// The shaped ones have their force on the horizontal axis
+				if (in_rate_x <  0.95)
+					in_rate_x = 0.95;
+			} else if ( (TREMOR        <= type)
+			         && (TECTONIC      >= type) ) {
+				if (in_rate_x < 0.25)
+					in_rate_x = 0.25;
+				if (in_rate_y < 0.25)
+					in_rate_y = 0.25;
+			}
+
+			// The full in_rate must not be lower than 10% on any weapon.
+			double in_rate = in_rate_x * in_rate_y;
+			if (in_rate < 0.1)
+				in_rate = 0.1;
+			dmg *= in_rate;
+		} // End of having damage to deal
+	} // End of tank in ellipse
+
+	return dmg;
 }
diff --git a/src/explosion.h b/src/explosion.h
index f7c9b19..2bcf4d4 100644
--- a/src/explosion.h
+++ b/src/explosion.h
@@ -24,45 +24,85 @@
 #include "main.h"
 #include "physobj.h"
 
-#define DIRT_CEILING 50
+class EXPLOSION: public PHYSICAL_OBJECT
+{
+public:
 
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
 
-class EXPLOSION: public PHYSICAL_OBJECT
-  {
-  public:
-    int	etime;
-    int	damage;
-    int	eframes;
-    int	radius;
-    int	sound;
-    int	exclock;
-    int	type, a;
-    int	peaked;
-    int	dispersing;
-    WEAPON	*weap;
-		/* to allow synchronous tank explosions, explosions need to know what they are */
-		bool bIsWeaponExplosion;
-
-    virtual ~EXPLOSION ();
-    EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, int weaponType);
-    EXPLOSION (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, double xvel, double yvel, int weaponType);
-    void	draw (BITMAP *dest);
-    void	initialise ();
-    int	applyPhysics ();
-    void	explode ();
-    int	isSubClass (int classNum);
-    inline virtual int	getClass ()
-    {
-      return (EXPLOSION_CLASS);
-    }
-    inline virtual void setEnvironment(ENVIRONMENT *env)
-    {
-      if (!_env || (_env != env))
-        {
-          _env = env;
-          _index = _env->addObject (this);
-        }
-    }
-  };
+	// default ctor for all non-BEAM explosions
+	explicit
+	EXPLOSION (PLAYER* player_, double x_, double y_, double xv_, double yv_,
+	           int32_t type, bool is_weapon);
+	// Special ctor for BEAM:
+	EXPLOSION (PLAYER* player_, double x_, double y_, double xv_, double yv_,
+	           int32_t type, double damage_, bool is_weapon);
+	~EXPLOSION ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void	 applyPhysics();
+    void	 draw        ();
+	void     explode     ();
+
+	eClasses getClass() { return CLASS_EXPLOSION; }
+
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void do_clear();
+	void do_throw();
+	void drawFracture (int32_t x, int32_t y, int32_t frac_angle, int32_t width,
+	                   int32_t segmentLength, int32_t maxRecurse,
+	                   int32_t recurseDepth);
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	bool    apply_damage = true;
+	int32_t curFrame     = 1;
+	float   centre_x     = EXPLO_CX;
+	float   centre_y     = EXPLO_CY;
+	int32_t damage       = 0;
+	int32_t etime        = 0;
+	int32_t exclock      = 0;
+	float   explo_h      = EXPLO_H;
+	float   explo_w      = EXPLO_W;
+	float   flame_w      = 0.f;
+	float   flame_h      = 0.f;
+	bool    hasCleared   = false;
+	int32_t hasDebris    = 0;
+	bool    hasSlid      = false;
+	bool    hasThrown    = false;
+	double  impact_xv    = 0.;
+	double  impact_yv    = 0.;
+	int32_t maxDebris    = 0;
+	int32_t maxFrame     = 0;
+	bool    peaked       = false;
+	int32_t radius       = 10;
+	float   scale        = 0.;
+};
+
+
+// Global helpers:
+void   draw_Napalm_Blob(VIRTUAL_OBJECT* blob, int32_t x, int32_t y,
+                        int32_t radius, int32_t frame);
+double get_hit_damage  (TANK* tank, weaponType type, int32_t hit_x,
+                        int32_t hit_y);
 
 #endif
diff --git a/src/extern/dirent.c b/src/extern/dirent.c
new file mode 100755
index 0000000..3f8d8ee
--- /dev/null
+++ b/src/extern/dirent.c
@@ -0,0 +1,152 @@
+/*
+
+    Implementation of POSIX directory browsing functions and types for Win32.
+
+    Author:  Kevlin Henney (kevlin at acm.org, kevlin at curbralan.com)
+    History: Created March 1997. Updated June 2003 and July 2012.
+    Rights:  See end of file.
+
+*/
+
+#include "dirent.h"
+#include <errno.h>
+#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
+
+struct DIR
+{
+    handle_type         handle; /* -1 for failed rewind */
+    struct _finddata_t  info;
+    struct dirent       result; /* d_name null iff first time */
+    char                *name;  /* null-terminated char string */
+};
+
+DIR *opendir(const char *name)
+{
+    DIR *dir = 0;
+
+    if(name && name[0])
+    {
+        size_t base_length = strlen(name);
+		size_t full_length = base_length;
+		const char *all = /* search pattern must end with suitable wildcard */
+            strchr("/\\", name[base_length - 1]) ? "*" : "/*";
+
+		full_length += strlen(all) + 1;
+
+        if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
+		   ( dir->name = (char *)malloc(full_length) ) != 0 )
+        {
+			strcpy_s(dir->name, full_length, name);
+			strcat_s(dir->name, full_length, all);
+
+            if((dir->handle =
+                (handle_type) _findfirst(dir->name, &dir->info)) != -1)
+            {
+                dir->result.d_name = 0;
+            }
+            else /* rollback */
+            {
+                free(dir->name);
+                free(dir);
+                dir = 0;
+            }
+        }
+        else /* rollback */
+        {
+            free(dir);
+            dir   = 0;
+            errno = ENOMEM;
+        }
+    }
+    else
+    {
+        errno = EINVAL;
+    }
+
+    return dir;
+}
+
+int closedir(DIR *dir)
+{
+    int result = -1;
+
+    if(dir)
+    {
+        if(dir->handle != -1)
+        {
+            result = _findclose(dir->handle);
+        }
+
+        free(dir->name);
+        free(dir);
+    }
+
+    if(result == -1) /* map all errors to EBADF */
+    {
+        errno = EBADF;
+    }
+
+    return result;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+    struct dirent *result = 0;
+
+    if(dir && dir->handle != -1)
+    {
+        if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
+        {
+            result         = &dir->result;
+            result->d_name = dir->info.name;
+        }
+    }
+    else
+    {
+        errno = EBADF;
+    }
+
+    return result;
+}
+
+void rewinddir(DIR *dir)
+{
+    if(dir && dir->handle != -1)
+    {
+        _findclose(dir->handle);
+        dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
+        dir->result.d_name = 0;
+    }
+    else
+    {
+        errno = EBADF;
+    }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+
+    Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
+
+    Permission to use, copy, modify, and distribute this software and its
+    documentation for any purpose is hereby granted without fee, provided
+    that this copyright and permissions notice appear in all copies and
+    derivatives.
+    
+    This software is supplied "as is" without express or implied warranty.
+
+    But that said, if there are any problems please get in touch.
+
+*/
diff --git a/src/extern/dirent.h b/src/extern/dirent.h
new file mode 100755
index 0000000..a02a0d8
--- /dev/null
+++ b/src/extern/dirent.h
@@ -0,0 +1,50 @@
+#ifndef DIRENT_INCLUDED
+#define DIRENT_INCLUDED
+
+/*
+
+    Declaration of POSIX directory browsing functions and types for Win32.
+
+    Author:  Kevlin Henney (kevlin at acm.org, kevlin at curbralan.com)
+    History: Created March 1997. Updated June 2003.
+    Rights:  See end of file.
+    
+*/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct DIR DIR;
+
+struct dirent
+{
+    char *d_name;
+};
+
+DIR           *opendir(const char *);
+int           closedir(DIR *);
+struct dirent *readdir(DIR *);
+void          rewinddir(DIR *);
+
+/*
+
+    Copyright Kevlin Henney, 1997, 2003. All rights reserved.
+
+    Permission to use, copy, modify, and distribute this software and its
+    documentation for any purpose is hereby granted without fee, provided
+    that this copyright and permissions notice appear in all copies and
+    derivatives.
+    
+    This software is supplied "as is" without express or implied warranty.
+
+    But that said, if there are any problems please get in touch.
+
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/externs.h b/src/externs.h
index 2a6a692..cbec34e 100644
--- a/src/externs.h
+++ b/src/externs.h
@@ -1,5 +1,6 @@
-#ifndef EXTERNS_DEFINE
-#define	EXTERNS_DEFINE
+#pragma once
+#ifndef ATANKS_SRC_EXTERNS_H_INCLUDED
+#define ATANKS_SRC_EXTERNS_H_INCLUDED
 
 /*
  * atanks - obliterate each other with oversize weapons
@@ -20,40 +21,50 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  * */
 
+#include "globaldata.h"
+#ifndef HAS_GLOBALDATA
+class GLOBALDATA;
+#endif // HAS_GLOBALDATA
 
-extern char	*errorMessage;
-extern int	errorX, errorY;
+#include "environment.h"
+#ifndef HAS_ENVIRONMENT
+class ENVIRONMENT;
+#endif // HAS_ENVIRONMENT
 
-extern int	WHITE, BLACK, PINK, COLOR[];
+#define CLOCK_MAX 10
 
-extern int	k;
-extern int	ctrlUsedUp;
+#ifndef ATANKS_SRC_ATANKS_CPP
 
-extern int	cclock,
-  fps, frames,
-  fi, lx, ly,
-  order[];
+// === The two most important things in the game: ;) ===
+extern GLOBALDATA  global;
+extern ENVIRONMENT env;
 
-//extern char	cacheCirclesBG;
-//extern char	ditherGradients;
-//extern int	startmoney;
-//extern int	turntype;
 
-extern double	height[];
-//extern int	landtable[];
+// === Defined colours used everywhere ===
+extern int32_t BLACK, BLUE, DARK_GREEN, DARK_GREY, DARK_RED, GREY, GREEN,
+               LIGHT_GREEN, LIME_GREEN, ORANGE, PINK, PURPLE, RED, SILVER,
+               TURQUOISE, WHITE, YELLOW;
 
-//extern int	steep, mheight, mbase;
-//extern double	msteep, smooth;
-//extern double	gravity;
-//extern double	windstrength, windvariation;
-//extern double	interest;
-//extern char	name[][11];
 
-extern int winner;
+// === General values that are globally used ===
+extern char        buf[100];
+extern const char* errorMessage;
+extern int32_t     errorX, errorY;
+extern int32_t     k, K;
+extern int32_t     fi, lx, ly;
+extern WEAPON      weapon[WEAPONS];    // from files.cpp
+extern WEAPON      naturals[NATURALS]; // from files.cpp
+extern ITEM        item[ITEMS];        // from files.cpp
 
-extern WEAPON	weapon[];
-extern WEAPON	naturals[];
-extern ITEM	item[];
 
-#endif
+// === Gradients ===
+extern gradient  topbar_gradient[4];
+extern gradient  stuff_bar_gradient[11];
+extern gradient  circles_gradient[4];
+extern gradient  explosion_gradient1[3];
+extern gradient  explosion_gradient2[3];
+extern gradient* explosion_gradients[2];
+
+#endif // ATANKS_SRC_ATANKS_CPP
+#endif // ATANKS_SRC_EXTERNS_H_INCLUDED
 
diff --git a/src/fade.cpp b/src/fade.cpp
deleted file mode 100644
index eb6da5a..0000000
--- a/src/fade.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * atanks - obliterate each other with oversize weapons
- * Copyright (C) 2003  Thomas Hudson
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- * */
-
-/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-fade.cc
-
-Provides graphical transitions via change and quickChange, as prototyped in
-main.h
-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
-
-/*
-TODO
- + Add more faders
- + Improve/complete documentation
-*/
-
-
-
-#include "globaldata.h"
-#include "main.h"
-
-
-
-/*****************************************************************************
-Internal fades
-
-The following section of code defines the internal fade routines.  These
-routines are used by the change function to perform transitions.  To add a
-new fader, simply implement a function as seen below and then add it to
-change's function pointer array.
-*****************************************************************************/
-/*
-static void spiralPixelFade (GLOBALDATA *global, BITMAP *target)
-{
-  int blockSize = 8;
-  int spiralSize = 20;
-  int length = spiralSize, x = 0, y = 0;
-  int stepSize = (spiralSize + 1) * blockSize;
-  int x2, y2;
-  int count;
-
-  for (count = 0, y = stepSize - blockSize; count < length; count++, y -= blockSize)
-    for (x2 = 0; x2 <= global->screenWidth; x2 += stepSize)
-      for (y2 = 0; y2 <= global->screenHeight; y2 += stepSize)
-        blit (target, screen, x2+x, y2+y, x2+x, y2+y, blockSize, blockSize);
-  while (length)
-    {
-      for (count = 0; count < length; count++, x += blockSize)
-        for (x2 = 0; x2 <= global->screenWidth; x2 += stepSize)
-          for (y2 = 0; y2 <= global->screenHeight; y2 += stepSize)
-            blit (target, screen, x2+x, y2+y, x2+x, y2+y, blockSize, blockSize);
-      for (count = 0; count < length; count++, y += blockSize)
-        for (x2 = 0; x2 <= global->screenWidth; x2 += stepSize)
-          for (y2 = 0; y2 <= global->screenHeight; y2 += stepSize)
-            blit (target, screen, x2+x, y2+y, x2+x, y2+y, blockSize, blockSize);
-      length--;
-      for (count = 0; count < length; count++, x -= blockSize)
-        for (x2 = 0; x2 <= global->screenWidth; x2 += stepSize)
-          for (y2 = 0; y2 <= global->screenHeight; y2 += stepSize)
-            blit (target, screen, x2+x, y2+y, x2+x, y2+y, blockSize, blockSize);
-      for (count = 0; count < length; count++, y -= blockSize)
-        for (x2 = 0; x2 <= global->screenWidth; x2 += stepSize)
-          for (y2 = 0; y2 <= global->screenHeight; y2 += stepSize)
-            blit (target, screen, x2+x, y2+y, x2+x, y2+y, blockSize, blockSize);
-      length--;
-    }
-  for (x2 = 0; x2 <= global->screenWidth; x2 += stepSize)
-    for (y2 = 0; y2 <= global->screenHeight; y2 += stepSize)
-      blit (target, screen, x2+x, y2+y, x2+x, y2+y, blockSize, blockSize);
-}
-
-static void topDownMultiWipeFade (GLOBALDATA *global, BITMAP *target)
-{
-  int z, zz ;
-
-  for (zz = 0; zz < 8; zz++)
-    {
-      int lineCount = 0;
-      for (z = zz; z < global->screenHeight; z += 8)
-        {
-          blit (target, screen, 0, z, 0, z, global->screenWidth, 1);
-          if (lineCount == 10)
-            {
-              lineCount = 0;
-              rest (1);
-            }
-          lineCount++;
-        }
-    }
-}
-
-static void topDownWipeFade (GLOBALDATA *global, BITMAP *target)
-{
-  int z;
-
-  for (z = 0; z <= global->screenHeight / 8; z++)
-    {
-      blit (target, screen, 0, z * 8, 0, z * 8, global->screenWidth, 8);
-      rest (1);
-    }
-}
-
-static void bottomUpWipeFade (GLOBALDATA *global, BITMAP *target)
-{
-  int z;
-
-  for (z = global->screenHeight / 8; z >= 0; z--)
-    {
-      blit (target, screen, 0, z * 8, 0, z * 8, global->screenWidth, 8);
-      rest (1);
-    }
-}
-
-static void rightLeftWipeFade (GLOBALDATA *global, BITMAP *target)
-{
-  int z;
-
-  for (z = global->screenWidth / 8; z >= 0; z--)
-    {
-      blit (target, screen, z * 8, 0, z * 8, 0, 8, global->screenHeight);
-      rest (1);
-    }
-}
-
-static void leftRightWipeFade (GLOBALDATA *global, BITMAP *target)
-{
-  int z;
-
-  for (z = 0; z <= global->screenWidth / 8; z++)
-    {
-      blit (target, screen, z * 8, 0, z * 8, 0, 8, global->screenHeight);
-      rest (1);
-    }
-}
-
-static void crossLinesFade (GLOBALDATA *global, BITMAP *target)
-{
-  int z;
-
-  for (z = 0; z < (global->screenWidth/4+4) / 4; z++)
-    {
-      blit (target, screen, z * 8, 0, z * 8, 0, 4, global->screenHeight);
-      blit (target, screen, (global->screenWidth-1) - (z * 8), 0, (global->screenWidth-1) - (z * 8), 0, 4, global->screenHeight);
-      blit (target, screen, 0, (z * 6), 0, (z * 6), global->screenWidth, 4);
-      blit (target, screen, 0, (global->screenHeight - (z * 6)), 0, (global->screenHeight - (z * 6)), global->screenWidth, 4);
-      rest (1);
-    }
-  for (z = (global->screenWidth/4+4) / 4; z != 0; z--)
-    {
-      blit (target, screen, z * 8 + 4, 0, z * 8 + 4, 0, 4, global->screenHeight);
-      blit (target, screen, (global->screenWidth-1) - (z * 8) + 4, 0, (global->screenWidth-1) - (z * 8) + 4, 0, 4, global->screenHeight);
-      blit (target, screen, 0, (z * 6) + 4, 0, (z * 6) + 4, global->screenWidth, 4);
-      blit (target, screen, 0, (global->screenHeight - (z * 6)) + 4, 0, (global->screenHeight - (z * 6)) + 4, global->screenWidth, 4);
-      rest (1);
-    }
-}
-
-*/
-
-/*****************************************************************************
-changeHandleError
-
-Used internally by change and quickChange.  Prints out error messages.
-*****************************************************************************/
-static void changeHandleError (GLOBALDATA *global, BITMAP *target)
-{
-  if (! global->os_mouse) show_mouse (NULL);
-  if (errorMessage)
-    {
-      textout_ex (target, font, errorMessage, errorX, errorY, makecol (255, 0, 0), -1);
-      errorMessage = NULL;
-    }
-}
-
-
-/*****************************************************************************
-change
-
-Selects and executes a random transition.
-*****************************************************************************/
-/*
-void change(GLOBALDATA *global, BITMAP *target)
-{
-  static void (* const procs[])(GLOBALDATA*,BITMAP*) =
-  {
-    crossLinesFade,			spiralPixelFade,
-    leftRightWipeFade,		rightLeftWipeFade,
-    bottomUpWipeFade,		topDownWipeFade,
-    topDownMultiWipeFade,
-  } ;
-  static const int cprocs = sizeof( procs ) / sizeof( procs[0] );
-
-  if (! global->os_mouse) show_mouse (NULL);
-  changeHandleError (global, target);
-
-  (procs[rand() % cprocs]) (global, target);
-
-  clear_keybuf ();
-}
-*/
-
-void change(GLOBALDATA *global, BITMAP *target)
-{
-    quickChange(global, target);
-}
-
-/*****************************************************************************
-quickChange
-
-Executes a fast and simple transition.
-
-*****************************************************************************/
-void quickChange (GLOBALDATA *global, BITMAP *target)
-{
-  changeHandleError (global, target);
-  blit (target, screen, 0, 0, 0, 0, global->screenWidth, global->screenHeight);
-  rest (1);
-}
-
diff --git a/src/files.cpp b/src/files.cpp
index cca9dc4..bb3842f 100644
--- a/src/files.cpp
+++ b/src/files.cpp
@@ -1,91 +1,103 @@
+#include "main.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <dirent.h>
+#include <cassert>
 
 // basically all UNIX-like OSes should use stat
 #ifndef WIN32
-#include <sys/stat.h>
+#  include <sys/stat.h>
 #endif
 
 #include "player.h"
 #include "files.h"
-#include "main.h"
 #include "text.h"
 
-/*
-This function saves the game in progress.
-All data is saved in a text file for flexiblity.
-Returns TRUE on success and FALSE on failure.
-*/
-int Save_Game(GLOBALDATA *global, ENVIRONMENT *env)
+
+// They are filled here, declaring them here prevents the linker
+// from 'optimizing' them away.
+WEAPON  weapon[WEAPONS];
+WEAPON  naturals[NATURALS];
+ITEM    item[ITEMS];
+
+
+
+/* Update: Instead of using constantly allocated/deallocated
+ * char strings for file path generation, one string buffer
+ * used everywhere that does not need *alloc/free is faster
+ * and a lot more secure.
+ * - Sven
+ */
+char path_buf[PATH_MAX + 1];
+// Note: Any consumer of this buffer has to include files.h or
+//       to define an extern for it.
+
+
+/** @brief Save the current game in progress
+  * This function saves the game in progress.
+  * All data is saved in a text file for flexibility.
+  * @return true on success and false on failure.
+**/
+bool Save_Game()
 {
-  FILE *game_file = NULL;
-  char *file_path = NULL;
-  int player_count = 0;
-  PLAYER *my_player = NULL;
-  int count;
-  int global_player_number;
-
-  // figure out file name
-  file_path = (char *) calloc( strlen(global->configDir) +
-                               strlen(global->game_name) + 24,
-                               sizeof(char) );
-  if (! file_path)
-    return FALSE;
-
-  sprintf(file_path, "%s/%s.sav", global->configDir, global->game_name);
-
-  game_file = fopen(file_path, "w");
-  free(file_path);
-  if (!game_file)
-    return FALSE;
-
-  // write global data
-  fprintf(game_file, "GLOBAL\n");
-  fprintf(game_file, "ROUNDS=%f\n", global->rounds);
-  fprintf(game_file, "CURRENTROUND=%d\n", global->currentround);
-  fprintf(game_file, "CAMPAIGNMODE=%f\n", global->campaign_mode);
-  fprintf(game_file, "***\n");
-
-  // write envrionment data
-  fprintf(game_file, "ENVIRONMENT\n");
-  fprintf(game_file, "***\n");
-
-  // write player data
-  fprintf(game_file, "PLAYERS\n");
-  while ( player_count < global->numPlayers )
-  {
-      my_player = global->players[player_count];
-      global_player_number = 0;
-      while ( strcmp(global->allPlayers[global_player_number]->getName(), my_player->getName() ) )
-        global_player_number++;
-      fprintf(game_file, "PLAYERNUMBER=%d\n", global_player_number);
-      fprintf(game_file, "SCORE=%d\n", my_player->score);
-      fprintf(game_file, "MONEY=%d\n", my_player->money);
-      fprintf(game_file, "TYPE=%f\n", my_player->type);
-      fprintf(game_file, "TYPESAVED=%f\n", my_player->type_saved);
-      for (count = 0; count < WEAPONS; count++)
-        fprintf(game_file, "WEAPON=%d %d\n", count, my_player->nm[count]);
-      for (count = 0; count < ITEMS; count++)
-        fprintf(game_file, "ITEM=%d %d\n", count, my_player->ni[count]);
-      fprintf(game_file, "***\n");
-      player_count++;
-    }
-
-  fclose(game_file);
-
-  // save the current config
-  file_path = (char *) calloc( strlen(global->configDir) +
-                               strlen(global->game_name) + 24,
-                               sizeof(char) );
-  if (! file_path)
-    return FALSE;
-  sprintf(file_path, "%s/%s.txt", global->configDir, global->game_name);
-  Save_Game_Settings_Text(global, env, file_path);
-  free(file_path);
-
-  return TRUE;
+	snprintf(path_buf, PATH_MAX, "%s/%s.sav", env.configDir, env.game_name);
+
+	FILE* game_file = fopen(path_buf, "w");
+	if (!game_file)
+		return false;
+
+	// write global data
+	fprintf(game_file, "GLOBAL\n");
+	fprintf(game_file, "CURRENTROUND=%d\n", global.currentround + 1);
+	// Note: When the game is saved, the round already has been decreased. Thus,
+	// to not loose rounds when saving a game, returning to main and load it
+	// again, this has to be increased here.
+	fprintf(game_file, "SCOREBOARD=%d\n", global.showScoreBoard ? 1 : 0);
+	fprintf(game_file, "***\n");
+
+	// write environment data
+	fprintf(game_file, "ENVIRONMENT\n");
+	fprintf(game_file, "CAMPAIGNMODE=%d\n", env.campaign_mode ? 1 : 0);
+	fprintf(game_file, "CAMPAIGNROUNDS=%lf\n", env.campaign_rounds);
+	fprintf(game_file, "NEXTCAMPROUND=%lf\n", env.nextCampaignRound);
+	fprintf(game_file, "ROUNDS=%u\n", env.rounds);
+	fprintf(game_file, "***\n");
+
+	// write player data
+	fprintf(game_file, "PLAYERS\n");
+	for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+		PLAYER* my_player = env.players[i];
+
+		if (my_player->index > -1) {
+			// Note: This line is needed to know which player to load
+			fprintf(game_file, "PLAYERNUMBER=%d\n", my_player->index);
+			my_player->save_game_data(game_file);
+		}
+	}
+
+	fprintf(game_file, "***EOF***\n");
+
+	fclose(game_file);
+
+	/* atanks always saved the current configuration in a file
+	 * alongside the save game, but this environment file was
+	 * never read. It makes no sense anyway, as it would change
+	 * the current settings without re-loading the main config
+	 * file.
+	 * However, I find this very good for testing reasons, and
+	 * if a game becomes boring, why not allow the player to
+	 * enable some weather effects before loading a game?
+	 * So it won't be added that the environment file is loaded,
+	 * but if an old one is laying around, we should delete our
+	 * own garbage:
+	 */
+	snprintf(path_buf, PATH_MAX, "%s/%s.txt",
+	         env.configDir, env.game_name);
+	if (!access(path_buf, F_OK) && !access(path_buf, W_OK))
+		unlink(path_buf);
+
+	return true;
 }
 
 
@@ -94,139 +106,183 @@ int Save_Game(GLOBALDATA *global, ENVIRONMENT *env)
 This function attempts to load a saved
 game.
 The function returns TRUE on success and
-FALSE is an error occures.
+FALSE if an error occurs.
 -- Jesse
 */
-int Load_Game(GLOBALDATA *global, ENVIRONMENT * /*env*/)
+bool Load_Game()
 {
-  char line[512];
-  char *field, *value;
-  int index, amount, player_count = 0;
-  FILE *my_file;
-  char *file_path;
-  int stage = NO_STAGE;
-  char *got_line;
-  int global_player_number;
-
-  // load config settings
-  /*
-  file_path = (char *) calloc( strlen(homedir) +
-                               strlen(global->game_name) + 24,
-                               sizeof(char) );
-  if (! file_path)
-    return FALSE;
-  sprintf(file_path, "%s/.atanks/%s.txt", homedir, global->game_name);
-  my_file = fopen(file_path, "r");
-  free(file_path);
-  if (! my_file)
-    return FALSE;
-  global->loadFromFile_Text(my_file);
-  env->loadFromFile_Text(my_file);
-  fclose(my_file);
-  */
-
-  file_path = (char *) calloc( strlen(global->configDir) +
-                               strlen(global->game_name) + 24,
-                               sizeof(char) );
-  if (! file_path)
-    return FALSE;
-
-  sprintf(file_path, "%s/%s.sav", global->configDir, global->game_name);
-
-  my_file = fopen(file_path, "r");
-  free(file_path);
-  if (! my_file)
-    return FALSE;
-
-  // read a line from the file
-  got_line = fgets(line, 512, my_file);
-  // keep reading until we hit EOF or error
-  while ( (got_line) && (player_count < MAXPLAYERS) )
-  {
-      // clear end of line
-      if ( strchr(line, '\n') )
-        strchr(line, '\n')[0] = '\0';
-      if ( strchr(line, '\r') )
-        strchr(line, '\r')[0] = '\0';
-
-      // check to see if we found a new stage
-      if (! strcasecmp(line, "global") )
-        stage = GLOBAL_STAGE;
-      else if (! strcasecmp(line, "environment") )
-        stage = ENVIRONMENT_STAGE;
-      else if (! strcasecmp(line, "players") )
-        stage = PLAYER_STAGE;
-
-      // not a new stage, seperate field and value
-      else
-        {
-          value = strchr(line, '=');
-          if (value)        // valid line
-            {
-              field = line;
-              value[0] = '\0';   // seperate field and value;
-              value++;           // go to first place after =
-              // interpret data
-              switch (stage)
-                {
-                case GLOBAL_STAGE:
-                  if (! strcasecmp(field, "rounds") )
-                    sscanf(value, "%lf", &global->rounds);
-                  else if (! strcasecmp(field, "currentround") )
-                    sscanf(value, "%d", &global->currentround);
-                  else if (! strcasecmp(field, "campaignmode") )
-                    sscanf(value, "%lf", &global->campaign_mode);
-
-                  break;
-                case ENVIRONMENT_STAGE:
-
-                  break;
-                case PLAYER_STAGE:
-                  if (! strcasecmp(field, "playernumber") )
-                    {
-                      sscanf(value, "%d", &global_player_number);
-                      global->addPlayer( global->allPlayers[global_player_number] );
-                      /*  the loading of the preferences is (unfortunately) disabled and I do not know why.
-                          As long as they *are* disabled, PerPlay-Config bots have to get a new config here,
-                          or they use the global config, rendering PerPlay-Config useless. */
-                      if	( (global->players[player_count]->preftype	==	PERPLAY_PREF)
-                           && (global->players[player_count]->type			!=	HUMAN_PLAYER))
-                        global->players[player_count]->generatePreferences();
-                      global->players[player_count]->initialise();
-                    }
-                  else if (! strcasecmp(field, "score") )
-                    sscanf(value, "%d", &(global->players[player_count]->score) );
-                  else if (! strcasecmp(field, "money") )
-                    sscanf(value, "%d", &(global->players[player_count]->money) );
-                  else if (! strcasecmp(field, "type") )
-                    sscanf(value, "%lf", &(global->players[player_count]->type) );
-                  else if (! strcasecmp(field, "typesaved") )
-                    sscanf(value, "%lf", &(global->players[player_count]->type_saved) );
-                  else if (! strcasecmp(field, "weapon") )
-                    {
-                      sscanf(value, "%d %d", &index, &amount);
-                      global->players[player_count]->nm[index] = amount;
-                    }
-                  else if (! strcasecmp(field, "item") )
-                    {
-                      sscanf(value, "%d %d", &index, &amount);
-                      global->players[player_count]->ni[index] = amount;
-                    }
-                  break;
-                }    // end of stage
-
-            }       //     end of valid line
-          else if ( (! strcmp(line, "***") ) && (stage == PLAYER_STAGE) )
-            player_count++;
-
-        }     // end of field value area
-
-      // read next line of file
-      got_line = fgets(line, 512, my_file);
-
-    }    // end of reading file
-  fclose(my_file);
-  return TRUE;
+	char    line[ MAX_CONFIG_LINE + 1] = { 0 };
+	char    field[MAX_CONFIG_LINE + 1] = { 0 };
+	char    value[MAX_CONFIG_LINE + 1] = { 0 };
+	char*   result                     = nullptr;
+	int32_t player_count               = 0;
+	int32_t line_num                   = 0;
+	int32_t player_idx                 = -1;
+	bool    done                       = false;
+
+	// Be sure that numbers are understood right:
+	const char* cur_lc_numeric = setlocale(LC_NUMERIC, "C");
+
+	// Ensure backward compatibility:
+	env.nextCampaignRound = -1.;
+	env.campaign_rounds   = -1.;
+
+	// Open game file
+	snprintf(path_buf, PATH_MAX, "%s/%s.sav", env.configDir, env.game_name);
+	FILE* game_file = fopen(path_buf, "r");
+	if (!game_file)
+		return false;
+
+	// Now read until the file is finished loading
+	eSaveGameStage stage = SGS_NONE;
+	do {
+		// read a line
+		memset(line, 0, MAX_CONFIG_LINE);
+		if ( ( result = fgets(line, MAX_CONFIG_LINE, game_file) ) ) {
+			++line_num;
+
+			// if we hit end of the file, stop
+			if (! strncmp(line, "***EOF***", 9) ) {
+				done = true;
+				continue; // This exits the loop as well
+			}
+
+			// strip newline character
+			int32_t line_length = strlen(line);
+			while ( line[line_length - 1] == '\n') {
+				line[line_length - 1] = '\0';
+				line_length--;
+			}
+
+			// check to see if we found a new stage
+			if (!strcasecmp(line, "GLOBAL") )
+				stage = SGS_GLOBAL;
+			else if (!strcasecmp(line, "ENVIRONMENT") )
+				stage = SGS_ENVIRONMENT;
+			else if (!strcasecmp(line, "PLAYERS") )
+				stage = SGS_PLAYERS;
+			else {
+				// Not a new stage, keep loading.
+
+				// find equal sign
+				int32_t equal_position = 1;
+				while ( ( equal_position < line_length )
+					 && ( line[equal_position] != '='  ) )
+					equal_position++;
+
+				// make sure the equal sign position is valid
+				if (line[equal_position] != '=')
+					continue; // Go to next line
+
+				// separate field from value
+				memset(field, '\0', MAX_CONFIG_LINE);
+				memset(value, '\0', MAX_CONFIG_LINE);
+				strncpy(field, line, equal_position);
+				strncpy(value, &( line[equal_position + 1] ), MAX_CONFIG_LINE);
+
+				switch (stage) {
+					case SGS_ENVIRONMENT:
+						if (!strcasecmp(field, "CAMPAIGNMODE") ) {
+							int32_t cm = 0;
+							sscanf(value, "%d", &cm);
+							env.campaign_mode = !cm ? false : true;
+						} else if (!strcasecmp(field, "CAMPAIGNROUNDS") )
+							sscanf(value, "%lf", &env.campaign_rounds);
+						else if (!strcasecmp(field, "ROUNDS") )
+							sscanf(value, "%u", &env.rounds);
+						else if (!strcasecmp(field, "NEXTCAMPROUND") )
+							sscanf(value, "%lf", &env.nextCampaignRound);
+						else {
+							cerr << path_buf << ":" << line_num << " : Ignored line\n";
+							cerr << "The line \"" << line << "\"";
+							cerr << " is ignored, it does not belong to ENV" << endl;
+						}
+
+						break;
+					case SGS_GLOBAL:
+						if (!strcasecmp(field, "CURRENTROUND") )
+							sscanf(value, "%u", &global.currentround);
+
+						// The following are kept for backwards compatibility:
+
+						else if (!strcasecmp(field, "NEXTCAMPROUND") )
+							sscanf(value, "%lf", &env.nextCampaignRound);
+						else if (!strcasecmp(field, "CAMPAIGNMODE") ) {
+							int32_t cm = 0;
+							sscanf(value, "%d", &cm);
+							env.campaign_mode = !cm ? false : true;
+						} else if (!strcasecmp(field, "ROUNDS") )
+							sscanf(value, "%u", &env.rounds);
+						else if (!strcasecmp(field, "SCOREBOARD") ) {
+							int32_t enabled = 0;
+							sscanf(value, "%d", &enabled);
+							global.showScoreBoard = (enabled != 0);
+						} else {
+							cerr << path_buf << ":" << line_num << " : Ignored line\n";
+							cerr << "The line \"" << line << "\"";
+							cerr << " is ignored, it does not belong to GLOBAL" << endl;
+						}
+
+						break;
+					case SGS_PLAYERS:
+						if (!strcasecmp(field, "PLAYERNUMBER") ) {
+							sscanf(value, "%d", &player_idx);
+							if ( (player_idx > -1)
+							  && (player_idx < env.numPermanentPlayers)
+							  && (player_count < MAXPLAYERS) ) {
+								env.addGamePlayer( env.allPlayers[player_idx] );
+								env.players[player_count++]->initialise(true);
+							} else
+								player_idx = -1;
+						}
+
+						// Now let the player load itself
+						if (player_idx > -1)
+							env.allPlayers[player_idx]->load_game_data(game_file);
+						else {
+							cerr << path_buf << ":" << line_num << " : Ignored line\n";
+							cerr << "The line \"" << line << "\"";
+							cerr << " is ignored, as player idx is " << player_idx << endl;
+						}
+
+						break;
+					case SGS_NONE:
+					default:
+						cerr << path_buf << ":" << line_num << " : Wrong line\n";
+						cerr << "The line \"" << line << "\"";
+						cerr << " does not belong  to any stage!" << endl;
+						break;
+				} // end of switching stage
+			} // End of loading line / player record
+		} // end of having read a line
+	} while (result && !done);
+	// End of being not done
+
+	fclose(game_file);
+
+	// See if the campaign state values have to be recalculated
+	// because an old save game was loaded.
+	if (env.campaign_rounds < 0.)
+		env.campaign_rounds = static_cast<double>(env.rounds) / 5.;
+	if (env.nextCampaignRound < 0.) {
+		env.nextCampaignRound = static_cast<double>(env.rounds)
+		                      - env.campaign_rounds;
+		while (global.currentround < env.nextCampaignRound)
+			env.nextCampaignRound -= env.campaign_rounds;
+	}
+
+	// To ensure backwards compatibility, all players have to check
+	// their opponents memory. Old save files do not provide any.
+    for (int32_t i = 0; i < env.numGamePlayers; ++i)
+		env.players[i]->checkOppMem();
+
+	// Revert locale settings
+	if (cur_lc_numeric)
+		setlocale(LC_NUMERIC, cur_lc_numeric);
+	else
+		setlocale(LC_NUMERIC, "");
+
+	return true;
 }
 
 
@@ -234,488 +290,410 @@ int Load_Game(GLOBALDATA *global, ENVIRONMENT * /*env*/)
 /*
 Check to see if a saved game exists with the given name.
 */
-int Check_For_Saved_Game(GLOBALDATA *global)
+bool Check_For_Saved_Game()
 {
-  FILE *my_file;
-  char *path_name;
-
-  path_name = (char *) calloc( strlen(global->configDir) + strlen(global->game_name) + 24,
-                               sizeof(char) );
-  if (! path_name)
-    return FALSE;
-
-  sprintf(path_name, "%s/%s.sav", global->configDir, global->game_name);
-
-  my_file = fopen(path_name, "r");
-  free(path_name);
-  if (my_file)
-  {
-     fclose(my_file);
-     return TRUE;
-  }
-  else
-    return FALSE;
+	snprintf(path_buf, PATH_MAX, "%s/%s.sav", env.configDir, env.game_name);
 
+	if (!access(path_buf, R_OK))
+		return true;
+
+	return false;
 }
 
 
 
-/*
-This function copies the atanks config file
-from the HOME_DIR folder to HOME_DIR/.atanks
-If the .atanks folder does not exist, this
-function will create it.
-Returns TRUE on success and FALSE on error.
+/** @brief Copy atanks config to safe location.
+  *
+  * This function copies the atanks config file from the HOME_DIR folder to
+  * HOME_DIR/.atanks
+  * If the .atanks folder does not exist, this function will create it.
+  *
+  * @return true on success, false otherwise
 */
-
-int Copy_Config_File(GLOBALDATA *global)
+bool Copy_Config_File()
 {
-  FILE *source_file, *dest_file;
-  char source_path[1024], dest_path[1024];
-  char buffer[1024];
-  char *my_home_folder;
-  int status;
-
-  // figure out where home is
-  my_home_folder = getenv(HOME_DIR);
-  if (! my_home_folder)
-    my_home_folder = (char *)".";
-
-  // check to see if the config file has already been copied
-  snprintf(source_path, 1024, "%s/atanks-config.txt", global->configDir);
-  source_file = fopen(source_path, "r");
-  if (source_file)     // config file is in the right place
-    {
-      fclose(source_file);
-      return TRUE;
-    }
-
-  /*
-  // check to make sure we have a source file
-  snprintf(source_path, 1024, "%s/.atanks-config.txt", my_home_folder);
-  source_file = fopen(source_path, "r");
-  if (! source_file)
-     return TRUE;
-  */
-
-  // file not copied yet, create the required directory
-  snprintf(buffer, 1024, "%s/.atanks", my_home_folder);
-#ifdef WIN32
-  status = mkdir(buffer);
+	static char xHere[2]            = ".";
+	FILE* source_file               = nullptr;
+	FILE* dest_file                 = nullptr;
+	char  source_path[PATH_MAX + 1] = { 0 };
+	char  dest_path[PATH_MAX + 1]   = { 0 };
+	char  buffer[PATH_MAX + 1]      = { 0 };
+
+	// check to see if the config file has already been copied
+	snprintf(dest_path, PATH_MAX, "%s/atanks-config.txt", env.configDir);
+	if (!access(dest_path, R_OK | W_OK))
+		return true;
+
+	char* my_home_folder        = getenv(HOME_DIR);
+	int   status                = 0;
+
+	// figure out where home is
+	if (! my_home_folder)
+		my_home_folder = xHere;
+
+
+	// file not copied yet, create the required directory
+	snprintf(buffer, PATH_MAX, "%s/.atanks", my_home_folder);
+#ifdef ATANKS_IS_WINDOWS
+	status = mkdir(buffer);
 #else
-  status = mkdir(buffer, 0700);
-#endif
-  if (status == -1)
-    {
-      printf( (char *)"Error occured. Unable to create sub directory.\n");
-      return FALSE;
-    }
-
-  // check to make sure we have a source file
-  snprintf(source_path, 1024, "%s/.atanks-config.txt", my_home_folder);
-  source_file = fopen(source_path, "r");
-  if (! source_file)
-    return TRUE;
-
-  // we already have an open source file, create destination file
-  snprintf(dest_path, 1024, "%s/atanks-config.txt", global->configDir);
-  dest_file = fopen(dest_path, "wb");
-  if (! dest_file)
-    {
-      printf( (char *)"Unable to create destination file.\n");
-      fclose(source_file);
-      return FALSE;
-    }
-
-  // we have open files, let's copy
-  status = fread(buffer, 1, 1024, source_file);
-  while (status)
-    {
-      status = fwrite(buffer, 1, 1024, dest_file);
-      status = fread(buffer, 1, 1024, source_file);
-    }
-
-  fclose(source_file);
-  fclose(dest_file);
-  return TRUE;
+	status = mkdir(buffer, 0700);
+#endif // ATANKS_IS_WINDOWS
+
+	if (status == -1) {
+		printf( "Error occured. Unable to create sub directory.\n");
+		return false;
+	}
+
+	// check to make sure we have a source file
+	snprintf(source_path, PATH_MAX, "%s/.atanks-config.txt", my_home_folder);
+	source_file = fopen(source_path, "r");
+	if (! source_file)
+		return true;
+
+	// we already have an open source file, create destination file
+	dest_file = fopen(dest_path, "wb");
+	if (! dest_file) {
+		printf( "Unable to create destination file.\n");
+		fclose(source_file);
+		return false;
+	}
+
+	// we have open files, let's copy
+	status = fread(buffer, 1, PATH_MAX, source_file);
+	while (status) {
+		status = fwrite(buffer, 1, PATH_MAX, dest_file);
+		status = fread(buffer, 1, PATH_MAX, source_file);
+	}
+
+	fclose(source_file);
+	fclose(dest_file);
+	return true;
 }
 
 
 
-// Make sure we have a music folder
-// Returns TRUE on success or FALSE
-// if an error occures.
-int Create_Music_Folder(GLOBALDATA *global)
+/** @brief Make sure we have a music folder
+  * @return true on success or false if an error occures.
+**/
+bool Create_Music_Folder()
 {
-   DIR *music_folder;
-   char *folder_path;
-   
-   folder_path = (char *) calloc( strlen(global->configDir) + 32, sizeof(char) );
-   if (! folder_path)
-      return FALSE;
-
-   sprintf(folder_path, "%s/music", global->configDir);
-   music_folder = opendir(folder_path);
-   if (! music_folder)
-   {
-     #ifdef WIN32
-     mkdir(folder_path);
-     #else
-     mkdir(folder_path, 0700);
-     #endif
-   }
-   else    // it already exists
-     closedir(music_folder);
-
-   free(folder_path);
-   return TRUE;
-}
+	snprintf(path_buf, PATH_MAX, "%s/music", env.configDir);
+	DIR* music_folder = opendir(path_buf);
 
-
-
-
-
-/*
-Displays lines of text.
-*/
-void renderTextLines (GLOBALDATA *global, ENVIRONMENT *env,
-                      TEXTBLOCK *lines, int scrollOffset,
-                      const FONT* fnt, const int spacing )
-{
-  const int textheight = text_height (fnt) ;
-  const int textheight_s = textheight * spacing ;
-  int yOffset = scrollOffset;
-  char *text;
-  int count;
-
-  for (count = 0;
-       (count < lines->total_lines) && (yOffset < global->screenHeight);
-       count++)
-    {
-      text = lines->complete_text[count];
-
-          textout_centre_ex (env->db, fnt, text,
-                             global->halfWidth + 2, global->halfHeight + yOffset + 2, BLACK, -1);
-          textout_centre_ex (env->db, fnt, text,
-                             global->halfWidth, global->halfHeight + yOffset, WHITE, -1);
-      yOffset += textheight_s;
-    }
+	if (! music_folder) {
+#ifdef ATANKS_IS_WINDOWS
+		if (mkdir(path_buf))
+#else
+		if (mkdir(path_buf, 0700))
+#endif // ATANKS_IS_WINDOWS
+			return false;
+	} else
+		// it already exists
+		closedir(music_folder);
+
+	return true;
 }
 
 
-
-
 /*
 Scroll text in a box
 */
-void scrollTextList (GLOBALDATA *global, ENVIRONMENT *env,
-                     TEXTBLOCK *lines)
+void scrollTextList (TEXTBLOCK* lines)
 {
-  /* Justin says: this function, along with renderTextLines, aren't
-  exactly efficient.  I think they could use rewrite. */
-  static const int numItemsSrc[] = { 100, 30 } ;
-
-  // DATAFILE *dffont ;
-  const FONT* fnt = font;
-  int spacing = 2;
-  int tOffset = rand ();
-  int itemType = rand () / (RAND_MAX/2 + 1) ;
-  int numItems = numItemsSrc[itemType];
-  int my_key, done = FALSE;
-  int moving = TRUE;
-
-  draw_circlesBG (global, env->db, 0, 0, global->screenWidth, global->screenHeight, false);
-  drawMenuBackground (global, env, BACKGROUND_BLANK, abs (tOffset), numItems);
-  quickChange (global, env->db);
-  //set_clip_rect (env->db, global->halfWidth - 300 + 1, 100 + 1,
-  //               global->halfWidth + 300 - 1, global->screenHeight - 100 - 1);
-  set_clip_rect(env->db, global->halfWidth - 300 + 1, global->menuBeginY + 1,
-                global->halfWidth + 300 - 1, global->menuEndY - 1);
-  int scrollOffset = 0;
-  flush_inputs ();
-  if (! global->os_mouse) show_mouse (NULL);
-
-  // lines->Display_All(TRUE);
-  do
-    {
-      drawMenuBackground (global, env, BACKGROUND_BLANK, abs (tOffset), numItems);
-
-      renderTextLines (global, env, lines, scrollOffset,
-                       fnt, spacing );
-      //blit (env->db, screen, global->halfWidth - 300, 100, global->halfWidth - 300, 100,
-      //      601, global->screenHeight - 199);
-      blit(env->db, screen, global->halfWidth - 300, global->menuBeginY,
-           global->halfWidth - 300, global->menuBeginY, 601, 
-           global->screenHeight - 2 * global->menuBeginY);
-      LINUX_REST;
-      if (moving)
-      {
-         tOffset++;
-         scrollOffset--;
-      }
-
-      if (scrollOffset < -(global->halfHeight - 100 + lines->total_lines * 30))
-        scrollOffset = global->halfHeight - 100;
-      if (global->close_button_pressed)
-        break;
-      if ( keypressed() )
-      {
-          my_key = (readkey()) >> 8;
-          switch (my_key)
-          {
-             case KEY_ESC: done = TRUE;
-                       break;
-             case KEY_SPACE: 
-                       moving = TRUE; 
-                       break;
-             case KEY_UP:
-                       tOffset--;
-                       scrollOffset++;
-                       moving = FALSE;
-                       break;
-             case KEY_DOWN:
-                       tOffset++;
-                       scrollOffset--;
-                       moving = FALSE;
-                       break;
-          }      // end of switch
-      }       // end of key pressed
-    }
-  while ( (! done) && (!mouse_b) );
-
-  if (! global->os_mouse) show_mouse (screen);
-  set_clip_rect (env->db, 0, 0, (global->screenWidth-1), (global->screenHeight-1));
-  flush_inputs ();
+	int32_t spacing  = 2;
+	int32_t tOffset  = (RAND_MAX / 4) + (rand() % (RAND_MAX / 4));
+	int32_t numItems = (rand() % 100) + 20;
+	int32_t key      = 0;
+	bool    done     = false;
+	bool    moving   = true;
+
+	eBackgroundTypes bgType = env.dynamicMenuBg
+	                        ? static_cast<eBackgroundTypes>(rand() % BACKGROUND_COUNT)
+	                        : BACKGROUND_BLANK;
+
+	drawMenuBackground (bgType, tOffset, numItems);
+	quickChange(true);
+
+	int32_t clip_l = env.halfWidth - 299;
+	int32_t clip_t = env.menuBeginY + 1;
+	int32_t clip_b = env.menuEndY - 1;
+	int32_t clip_r = env.halfWidth + 299;
+	set_clip_rect(global.canvas, clip_l, clip_t, clip_r, clip_b);
+
+	int32_t scrollOffset = clip_b - env.halfHeight - 14;
+	flush_inputs ();
+	SHOW_MOUSE(nullptr)
+
+	do {
+		if (global.isCloseBtnPressed())
+			done = true;
+
+		if (++tOffset >= INT_MAX)
+			tOffset = 0;
+		if (moving)
+			scrollOffset--;
+
+		if (scrollOffset < -(env.halfHeight - 100 + lines->Lines() * 30) )
+			scrollOffset = env.halfHeight - 100;
+		else if (scrollOffset > (clip_b - env.halfHeight - 14))
+			scrollOffset = clip_b - env.halfHeight - 14;
+
+		drawMenuBackground (bgType, tOffset, numItems);
+		lines->Render_Lines(scrollOffset, spacing, clip_t, clip_b);
+		global.make_update(env.halfWidth - 300, env.menuBeginY,
+							601, env.screenHeight - 2 * env.menuBeginY);
+		global.do_updates();
+		LINUX_REST;
+
+		if ( keypressed() ) {
+			key = (readkey()) >> 8;
+			switch (key) {
+				case KEY_ESC:
+					done = true;
+					break;
+				case KEY_SPACE:
+					moving = true;
+					break;
+				case KEY_UP:
+					scrollOffset += 2;
+					moving = false;
+					break;
+				case KEY_DOWN:
+					scrollOffset -= 2;
+					moving = false;
+					break;
+			}      // end of switch
+		}       // end of key pressed
+
+		if (mouse_b)
+			done = true;
+	} while (!done);
+
+	SHOW_MOUSE(global.canvas)
+	set_clip_rect (global.canvas, 0, 0, (env.screenWidth-1), (env.screenHeight-1));
+	flush_inputs ();
 }
 
 
-
 /* Flush key buffer and waits for button releases */
 void flush_inputs()
 {
-  do { }
-  while (mouse_b);
-  clear_keybuf();
+	do { std::this_thread::yield(); } while (mouse_b);
+	clear_keybuf();
 }
 
 
-
 // This file loads weapons, naturals and items
 // from a text file
 // Returns TRUE on success and FALSE on failure
-int Load_Weapons_Text(GLOBALDATA *global)
+bool Load_Weapons_Text()
 {
-  FILE *wfile;
-  char *path_to_file;
-  char *status;
-  char line[512];
-  int file_stage = 0;     // weapons, natruals, items
-  int data_stage = 0;         // name, descrption, data
-  int item_count = 0, weapon_count = 0, natural_count = 0;
-
-  setlocale(LC_NUMERIC, "C");
-  // get path name
-  path_to_file = (char *) calloc( strlen(global->dataDir) + 64, sizeof(char) );
-  if (! path_to_file)
-    {
-      printf( "Memory error occured while loading weapons.\n");
-      return FALSE;
-    }
-
-  if (global->language == LANGUAGE_ENGLISH)
-    sprintf(path_to_file, "%s/text/weapons.txt", global->dataDir);
-  else if (global->language == LANGUAGE_PORTUGUESE)
-    sprintf(path_to_file, "%s/text/weapons.pt_BR.txt", global->dataDir);
-  else if (global->language == LANGUAGE_FRENCH)
-    sprintf(path_to_file, "%s/text/weapons_fr.txt", global->dataDir);
-  else if (global->language == LANGUAGE_GERMAN)
-    sprintf(path_to_file, "%s/text/weapons_de.txt", global->dataDir);
-  else if (global->language == LANGUAGE_SLOVAK)
-    sprintf(path_to_file, "%s/text/weapons_sk.txt", global->dataDir);
-  else if (global->language == LANGUAGE_RUSSIAN)
-    sprintf(path_to_file, "%s/text/weapons_ru.txt", global->dataDir);
-  else if (global->language == LANGUAGE_SPANISH)
-    sprintf(path_to_file, "%s/text/weapons_ES.txt", global->dataDir);
-  else if (global->language == LANGUAGE_ITALIAN)
-    sprintf(path_to_file, "%s/text/weapons_it.txt", global->dataDir);
-
-  // open file
-  wfile = fopen(path_to_file, "r");
-  // free memory
-  // free(path_to_file);
-  if (! wfile)
-    {
-      printf( "Unable to open weapons file. (%s)\n", path_to_file);
-      free(path_to_file);
-      return FALSE;
-    }
-  free(path_to_file);
-
-  Clear_Weapons();            // make sure arrays are cleared before loading data
-  // read line
-  status = fgets(line, 512, wfile);
-  while (status)
-    {
-      // clear end of line
-      if ( strchr(line, '\n') )
-        strchr(line, '\n')[0] = '\0';
-      if ( strchr(line, '\r') )
-        strchr(line, '\r')[0] = '\0';
-
-      // skip # and empty lines
-      if ( (! (line[0] == '#') ) && ( strlen(line) > 2 ) )
-        {
-          // check for header
-          if (! strcasecmp(line, "*WEAPONS*") )
-            {
-              file_stage = 0;
-              data_stage = 0;
-            }
-          else if (! strcasecmp(line, "*NATURALS*") )
-            {
-              file_stage = 1;
-              data_stage = 0;
-            }
-          else if (! strcasecmp(line, "*ITEMS*") )
-            {
-              file_stage = 2;
-              data_stage = 0;
-            }
-
-          // not a special line, let's read some data
-          else
-            {
-              // weapon
-              if ( (file_stage == 0) && (weapon_count < WEAPONS) )
-                {
-                  if (data_stage == 0)   // name
-                    strcpy(weapon[weapon_count].name, line);
-                  else if (data_stage == 1)
-                    strcpy(weapon[weapon_count].description, line);
-                  else if (data_stage == 2)
-                    {
-                      sscanf(line, "%d %d %lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %lf %d %lf %lf %lf %d %lf",
-                             &(weapon[weapon_count].cost),
-                             &(weapon[weapon_count].amt),
-                             &(weapon[weapon_count].mass),
-                             &(weapon[weapon_count].drag),
-                             &(weapon[weapon_count].radius),
-                             &(weapon[weapon_count].sound),
-                             &(weapon[weapon_count].etime),
-                             &(weapon[weapon_count].damage),
-                             &(weapon[weapon_count].eframes),
-                             &(weapon[weapon_count].picpoint),
-                             &(weapon[weapon_count].spread),
-                             &(weapon[weapon_count].delay),
-                             &(weapon[weapon_count].noimpact),
-                             &(weapon[weapon_count].techLevel),
-                             &(weapon[weapon_count].warhead),
-                             &(weapon[weapon_count].numSubmunitions),
-                             &(weapon[weapon_count].submunition),
-                             &(weapon[weapon_count].impartVelocity),
-                             &(weapon[weapon_count].divergence),
-                             &(weapon[weapon_count].spreadVariation),
-                             &(weapon[weapon_count].launchSpeed),
-                             &(weapon[weapon_count].speedVariation),
-                             &(weapon[weapon_count].countdown),
-                             &(weapon[weapon_count].countVariation) );
-                    }
-                  data_stage++;
-                  if (data_stage > 2)
-                    {
-                      data_stage = 0;
-                      weapon_count++;
-                    }
-                }       // end of weapon section
-
-              // naturals
-              else if ( (file_stage == 1) && (natural_count < NATURALS) )
-                {
-                  if (data_stage == 0)   // name
-                    strcpy(naturals[natural_count].name, line);
-                  else if (data_stage == 1)
-                    strcpy(naturals[natural_count].description, line);
-                  else if (data_stage == 2)
-                    {
-                      sscanf(line, "%d %d %lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %lf %d %lf %lf %lf %d %lf",
-                             &(naturals[natural_count].cost),
-                             &(naturals[natural_count].amt),
-                             &(naturals[natural_count].mass),
-                             &(naturals[natural_count].drag),
-                             &(naturals[natural_count].radius),
-                             &(naturals[natural_count].sound),
-                             &(naturals[natural_count].etime),
-                             &(naturals[natural_count].damage),
-                             &(naturals[natural_count].eframes),
-                             &(naturals[natural_count].picpoint),
-                             &(naturals[natural_count].spread),
-                             &(naturals[natural_count].delay),
-                             &(naturals[natural_count].noimpact),
-                             &(naturals[natural_count].techLevel),
-                             &(naturals[natural_count].warhead),
-                             &(naturals[natural_count].numSubmunitions),
-                             &(naturals[natural_count].submunition),
-                             &(naturals[natural_count].impartVelocity),
-                             &(naturals[natural_count].divergence),
-                             &(naturals[natural_count].spreadVariation),
-                             &(naturals[natural_count].launchSpeed),
-                             &(naturals[natural_count].speedVariation),
-                             &(naturals[natural_count].countdown),
-                             &(naturals[natural_count].countVariation) );
-                    }
-
-                  data_stage++;
-                  if (data_stage > 2)
-                    {
-                      data_stage = 0;
-                      natural_count++;
-                    }
-
-                }       // end of naturals
-
-              // item section
-              else if ( (file_stage == 2) && (item_count < ITEMS) )
-                {
-                  if (data_stage == 0)   // name
-                    strcpy(item[item_count].name, line);
-                  else if (data_stage == 1)
-                    strcpy(item[item_count].description, line);
-                  else if (data_stage == 2)
-                    {
-                      sscanf(line, "%d %d %d %d %d %lf %lf %lf %lf %lf %lf",
-                             &(item[item_count].cost),
-                             &(item[item_count].amt),
-                             &(item[item_count].selectable),
-                             &(item[item_count].techLevel),
-                             &(item[item_count].sound),
-                             &(item[item_count].vals[0]),
-                             &(item[item_count].vals[1]),
-                             &(item[item_count].vals[2]),
-                             &(item[item_count].vals[3]),
-                             &(item[item_count].vals[4]),
-                             &(item[item_count].vals[5]) );
-                    }
-
-                  data_stage++;
-                  if (data_stage > 2)
-                    {
-                      data_stage = 0;
-                      item_count++;
-                    }
-                }         // end of items
-
-            }       // end of reading data from a valid line
-
-        }     // end of valid line
-
-      // read in data
-      status = fgets(line, 512, wfile);
-    }
-
-  // close file
-  fclose(wfile);
-  // go home
-  return TRUE;
+	// Be sure that numbers are understood right:
+	const char* cur_lc_numeric = setlocale(LC_NUMERIC, "C");
+
+	// get path name
+	if (env.language == EL_ENGLISH)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons.txt", env.dataDir);
+	else if (env.language == EL_PORTUGUESE)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons.pt_BR.txt", env.dataDir);
+	else if (env.language == EL_FRENCH)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons_fr.txt", env.dataDir);
+	else if (env.language == EL_GERMAN)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons_de.txt", env.dataDir);
+	else if (env.language == EL_SLOVAK)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons_sk.txt", env.dataDir);
+	else if (env.language == EL_RUSSIAN)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons_ru.txt", env.dataDir);
+	else if (env.language == EL_SPANISH)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons_ES.txt", env.dataDir);
+	else if (env.language == EL_ITALIAN)
+		snprintf(path_buf, PATH_MAX, "%s/text/weapons_it.txt", env.dataDir);
+
+	// open file
+	FILE* wfile = fopen(path_buf, "r");
+
+	if (! wfile) {
+		printf( "Unable to open weapons file. (%s)\n", path_buf);
+		return false;
+	}
+
+	// read line
+	char       line[512]     = { 0 };
+	char*      status        = fgets(line, 512, wfile);
+	eFileStage file_stage    = FS_WEAPONS; // weapons, naturals, items
+	eDataStage data_stage    = DS_NAME;    // name, description, data
+	int32_t    item_count    = 0;
+	int32_t    weapon_count  = 0;
+	int32_t    natural_count = 0;
+
+	while (status) {
+		// clear end of line
+		if ( strchr(line, '\n') )
+			strchr(line, '\n')[0] = '\0';
+		if ( strchr(line, '\r') )
+			strchr(line, '\r')[0] = '\0';
+
+		// skip # and empty lines
+		if ( (! (line[0] == '#') ) && ( strlen(line) > 2 ) ) {
+
+			// check for header
+			if (!strcasecmp(line, "*WEAPONS*") ) {
+				file_stage = FS_WEAPONS;
+				data_stage = DS_NAME;
+			} else if (!strcasecmp(line, "*NATURALS*") ) {
+				file_stage = FS_NATURALS;
+				data_stage = DS_NAME;
+			} else if (!strcasecmp(line, "*ITEMS*") ) {
+				file_stage = FS_ITEMS;
+				data_stage = DS_NAME;
+			}
+
+			// not a special line, let's read some data
+			else {
+				// =============
+				// == Weapons ==
+				// =============
+				if ( (FS_WEAPONS == file_stage) && (weapon_count < WEAPONS) ) {
+					if (DS_NAME == data_stage)
+						weapon[weapon_count].setName(line);
+					else if (DS_DESC == data_stage)
+						weapon[weapon_count].setDesc(line);
+					else if (DS_DATA == data_stage) {
+						sscanf(line, "%d %d %lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %lf %d %lf %lf %lf %d %lf",
+						       &(weapon[weapon_count].cost),
+						       &(weapon[weapon_count].amt),
+						       &(weapon[weapon_count].mass),
+						       &(weapon[weapon_count].drag),
+						       &(weapon[weapon_count].radius),
+						       &(weapon[weapon_count].sound),
+						       &(weapon[weapon_count].etime),
+						       &(weapon[weapon_count].damage),
+						       &(weapon[weapon_count].picpoint),
+						       &(weapon[weapon_count].spread),
+						       &(weapon[weapon_count].delay),
+						       &(weapon[weapon_count].noimpact),
+						       &(weapon[weapon_count].techLevel),
+						       &(weapon[weapon_count].warhead),
+						       &(weapon[weapon_count].numSubmunitions),
+						       &(weapon[weapon_count].submunition),
+						       &(weapon[weapon_count].impartVelocity),
+						       &(weapon[weapon_count].divergence),
+						       &(weapon[weapon_count].spreadVariation),
+						       &(weapon[weapon_count].launchSpeed),
+						       &(weapon[weapon_count].speedVariation),
+						       &(weapon[weapon_count].countdown),
+						       &(weapon[weapon_count].countVariation) );
+					}
+
+					// Advance data stage
+					++data_stage;
+					if ( (DS_NAME == data_stage) // flipped
+					  || ( (DS_DATA    == data_stage)
+					    && (EL_ENGLISH != env.language)) ) {
+						data_stage = DS_NAME;
+						weapon_count++;
+					}
+				}       // end of weapon section
+
+				// ==============
+				// == Naturals ==
+				// ==============
+				else if ( (FS_NATURALS == file_stage) && (natural_count < NATURALS) ) {
+					if (DS_NAME == data_stage)
+						naturals[natural_count].setName(line);
+					else if (DS_DESC == data_stage)
+						naturals[natural_count].setDesc(line);
+					else if (DS_DATA == data_stage) {
+						sscanf(line, "%d %d %lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %lf %d %lf %lf %lf %d %lf",
+						       &(naturals[natural_count].cost),
+						       &(naturals[natural_count].amt),
+						       &(naturals[natural_count].mass),
+						       &(naturals[natural_count].drag),
+						       &(naturals[natural_count].radius),
+						       &(naturals[natural_count].sound),
+						       &(naturals[natural_count].etime),
+						       &(naturals[natural_count].damage),
+						       &(naturals[natural_count].picpoint),
+						       &(naturals[natural_count].spread),
+						       &(naturals[natural_count].delay),
+						       &(naturals[natural_count].noimpact),
+						       &(naturals[natural_count].techLevel),
+						       &(naturals[natural_count].warhead),
+						       &(naturals[natural_count].numSubmunitions),
+						       &(naturals[natural_count].submunition),
+						       &(naturals[natural_count].impartVelocity),
+						       &(naturals[natural_count].divergence),
+						       &(naturals[natural_count].spreadVariation),
+						       &(naturals[natural_count].launchSpeed),
+						       &(naturals[natural_count].speedVariation),
+						       &(naturals[natural_count].countdown),
+						       &(naturals[natural_count].countVariation) );
+					}
+
+					// Advance data stage
+					++data_stage;
+					if ( (DS_NAME == data_stage) // flipped
+					  || ( (DS_DATA    == data_stage)
+					    && (EL_ENGLISH != env.language)) ) {
+						data_stage = DS_NAME;
+						natural_count++;
+					}
+
+				}       // end of naturals
+
+				// ==============
+				// == Items ==
+				// ==============
+				else if ( (FS_ITEMS == file_stage) && (item_count < ITEMS) ) {
+					if (DS_NAME == data_stage)
+						item[item_count].setName(line);
+					else if (DS_DESC == data_stage)
+						item[item_count].setDesc(line);
+					else if (DS_DATA == data_stage) {
+						sscanf(line, "%d %d %d %d %d %lf %lf %lf %lf %lf %lf",
+						       &(item[item_count].cost),
+						       &(item[item_count].amt),
+						       &(item[item_count].selectable),
+						       &(item[item_count].techLevel),
+						       &(item[item_count].sound),
+						       &(item[item_count].vals[0]),
+						       &(item[item_count].vals[1]),
+						       &(item[item_count].vals[2]),
+						       &(item[item_count].vals[3]),
+						       &(item[item_count].vals[4]),
+						       &(item[item_count].vals[5]) );
+					}
+
+					// Advance data stage
+					++data_stage;
+					if ( (DS_NAME == data_stage) // flipped
+					  || ( (DS_DATA    == data_stage)
+					    && (EL_ENGLISH != env.language)) ) {
+						data_stage = DS_NAME;
+						item_count++;
+					}
+				}         // end of items
+			}       // end of reading data from a valid line
+		}     // end of valid line
+
+		// read in data
+		status = fgets(line, 512, wfile);
+	} // end while(status)
+
+
+	// close file
+	if (wfile)
+		fclose(wfile);
+
+
+	// Revert locale settings
+	if (cur_lc_numeric)
+		setlocale(LC_NUMERIC, cur_lc_numeric);
+	else
+		setlocale(LC_NUMERIC, "");
+
+	return true;
 }
 
 
@@ -730,36 +708,35 @@ int Filter_File( const struct dirent *my_file )
 int Filter_File( struct dirent *my_file )
 #endif
 {
-  if ( strstr(my_file->d_name, ".sav") )
-    return TRUE;
-  else
-    return FALSE;
+	if ( strstr(my_file->d_name, ".sav") )
+		return true;
+	else
+		return false;
 }
 
 /*
 This function finds a list of saved games on your profile.
 On error, NULL is returned. If all goes well, a list of file names
 are returned.
-After use, the return value should be freed.
+After use, the return value *must* be freed.
 */
-#ifndef WIN32
-struct dirent ** Find_Saved_Games(GLOBALDATA *global, int *num_files_found)
-  {
-    struct dirent **my_list;
-    int status;
-
-    status = scandir(global->configDir, &my_list, Filter_File, alphasort);
-    if (status < 0)
-      {
-        printf( (char *)"Error trying to find saved games.\n");
-        return NULL;
-      }
-
-    *num_files_found = status;
-    return my_list;
-  }
-#endif
+#if defined(ATANKS_IS_LINUX)
+dirent** Find_Saved_Games(uint32_t &num_files_found)
+{
+	dirent** my_list = nullptr;
+	int32_t  status  = 0;
 
+	status = scandir(env.configDir, &my_list, Filter_File, alphasort);
+	if (status < 0) {
+		printf("Error trying to find saved games.\n");
+		return nullptr;
+	}
+
+	num_files_found = status;
+
+	return my_list;
+}
+#endif //Linux
 
 
 /*
@@ -767,128 +744,40 @@ This function hunts for saved games. If games are found, the
 function returns an array of filenames. If an error occures
 or no files are found, NULL is returned.
 */
-#ifdef WIN32
-struct dirent ** Find_Saved_Games(GLOBALDATA *global, int *num_files_found)
-  {
-    struct dirent **my_list;
-    struct dirent *one_file;
-    int file_count = 0;
-    DIR *game_dir;
-
-    my_list = (struct dirent **) calloc(256, sizeof(struct dirent *));
-    if (! my_list)
-      return NULL;
-
-    game_dir = opendir(global->configDir);
-    if (! game_dir)
-    {
-      free(my_list);
-      return NULL;
-    }
-
-    one_file = readdir(game_dir);
-    while ( (one_file) && (file_count < 256) )
-      {
-        // check to see if this is a save game file
-        if ( strstr( one_file->d_name, ".sav" ) )
-          {
-            my_list[file_count] = (struct dirent *) calloc(1, sizeof(struct dirent) );
-            if ( my_list[file_count] )
-              {
-                memcpy(my_list[file_count], one_file, sizeof(struct dirent) );
-                file_count++;
-              }
-          }
-
-        one_file = readdir(game_dir);
-      }
-
-    closedir(game_dir);
-    *num_files_found = file_count;
-    return my_list;
-  }
-#endif
-
-
-
-
-/*
- * This function grabs a random quote from the war_quotes.txt file.
- * This file is held in the data directory (dataDir) and contains
- * one-line quotes.
- * The function returns a pointer to the quote on success and a NULL
- * on failure. The line should be free()ed after use.
- * -- Jesse
- *  */
-/*
-char *Get_Random_Quote(GLOBALDATA *global)
+#if defined(ATANKS_IS_WINDOWS)
+dirent** Find_Saved_Games(uint32_t &num_files_found)
 {
-    char *path_to_file = NULL;
-    char *line, *result;
-    FILE *quote_file = NULL;
-    int line_count = 0;
-    int index = 0;
-    int random_number;
-
-    line = (char *) calloc(1024, sizeof(char));
-    if (! line)
-       return NULL;
-    path_to_file = (char *) calloc( strlen(global->dataDir) + 32, sizeof(char) );
-    if (! path_to_file)
-    {
-       free(line);
-       return NULL;
-    }
-
-    if (global->language == LANGUAGE_RUSSIAN)
-       sprintf(path_to_file, "%s/war_quotes_ru.txt", global->dataDir);
-    else
-       sprintf(path_to_file, "%s/war_quotes.txt", global->dataDir);
-    quote_file = fopen(path_to_file, "r");
-    free(path_to_file);
-    if (! quote_file)
-    {
-       free(line);
-       return NULL;
-    }
-
-    // search through file getting the number of lines
-    result = fgets(line, 1024, quote_file);
-    while (result)
-    {
-        line_count++;
-        result = fgets(line, 1024, quote_file);
-    }
-
-    // return to the start of the file
-    rewind(quote_file);
-
-    // generate a random number based on the size of the file
-    random_number = rand() % line_count;
-
-    // search through and find the line we want
-    result = fgets(line, 1024, quote_file);
-    while ( (result) && (index < random_number) )
-    {
-        index++;
-        result = fgets(line, 1024, quote_file);
-    }
-
-    fclose(quote_file);
-    if (! result)
-    {
-       free(line);
-       return NULL;
-    }
-
-    // trim trailing newline character
-    if ( strchr(line, '\n') )
-       strchr(line, '\n')[0] = '\0';
-    return line;
+	dirent** my_list    = (dirent**)calloc(256, sizeof(dirent*));
+	dirent*  one_file   = nullptr;
+	uint32_t file_count = 0;
+	DIR*     game_dir   = nullptr;
+
+	if (!my_list)
+		return nullptr;
+
+	game_dir = opendir(env.configDir);
+	if (!game_dir) {
+		free(my_list);
+		return nullptr;
+	}
+
+	while ( (one_file = readdir(game_dir)) && (file_count < 256) ) {
+		// check to see if this is a save game file
+		if ( strstr( one_file->d_name, ".sav" ) ) {
+			my_list[file_count] = (dirent*)calloc(1, sizeof(dirent) );
+			if ( my_list[file_count] ) {
+				memcpy(my_list[file_count], one_file, sizeof(dirent));
+				my_list[file_count]->d_name = strdup(one_file->d_name);
+				file_count++;
+			}
+		}
+	}
+
+	closedir(game_dir);
+	num_files_found = file_count;
+	return my_list;
 }
-*/
-
-
+#endif // ATANKS_IS_WINDOWS
 
 
 /*
@@ -896,214 +785,52 @@ char *Get_Random_Quote(GLOBALDATA *global)
  * The function returns an array of bitmap file names. If no files
  * are found, or an error occures, then NULL is returned.
  * */
-char ** Find_Bitmaps(GLOBALDATA *global, int *bitmaps_found)
+char** Find_Bitmaps(int32_t* bitmaps_found)
 {
-    char **my_list;
-    struct dirent *one_file;
-    int file_count = 0;
-    DIR *game_dir;
-
-    my_list = (char **) calloc(256, sizeof(struct dirent *));
-    if (! my_list)
-      return NULL;
-
-    
-    game_dir = opendir(global->configDir);
-    if (! game_dir)
-    {
-      free(my_list);
-      return NULL;
-    }
-
-    one_file = readdir(game_dir);
-    while ( (one_file) && (file_count < 256) )
-    {
-         // check to see if this is a save game file
-         #ifdef LINUX
-         if ( strcasestr( one_file->d_name, ".bmp" ) )
-         #else
-         if ( (strstr(one_file->d_name, ".bmp")) || (strstr(one_file->d_name, ".BMP") ) )
-         #endif
-         {
-            my_list[file_count] = (char *) calloc( strlen(global->configDir) + strlen(one_file->d_name) + 16, sizeof(char) );
-            if ( my_list[file_count] )
-            {
-               sprintf(my_list[file_count], "%s/%s", global->configDir, one_file->d_name);
-               file_count++;
-            }
-         }
-
-         one_file = readdir(game_dir);
-     }
-        
-     closedir(game_dir);
-     *bitmaps_found = file_count;
-     if (file_count < 1)
-     {
-         free(my_list);
-         my_list = NULL;
-     }
-     return my_list;
-}
-
-
-
-
-
-BITMAP *create_gradient_strip (const gradient *grad, int length)
-{
-  BITMAP *strip;
-  int color;
-  int currLine;
-
-  strip = create_bitmap (1, length);
-  if (! strip)
-     return NULL;
-
-  clear_to_color (strip, BLACK);
-
-  for (currLine = 0;currLine < length; currLine++)
-    {
-      color = gradientColorPoint (grad, length, currLine);
-      putpixel (strip, 0, currLine, color);
-    }
-
-  return (strip);
-}
-
-
-
-
-int gradientColorPoint (const gradient *grad, double length, double line)
-{
-  int pointCount = 0;
-  double point = line / length;
-  int color;
-
-  for (pointCount = 0; (point >= grad[pointCount].point) && (grad[pointCount].point != -1); pointCount++) { }
-  pointCount--;
-
-  if (pointCount == -1)
-    {
-      color = makecol (grad[0].color.r, grad[0].color.g, grad[0].color.b);
-    }
-  else if (grad[pointCount + 1].point == -1)
-    {
-      color = makecol (grad[pointCount].color.r, grad[pointCount].color.g, grad[pointCount].color.b);
-    }
-  else
-    {
-      double i = (point - grad[pointCount].point) / (grad[pointCount + 1].point - grad[pointCount].point);
-      int r = (int)(interpolate (grad[pointCount].color.r, grad[pointCount + 1].color.r, i));
-      int g = (int)(interpolate (grad[pointCount].color.g, grad[pointCount + 1].color.g, i));
-      int b = (int)(interpolate (grad[pointCount].color.b, grad[pointCount + 1].color.b, i));
-
-      color = makecol (r, g, b);
-    }
-
-  return (color);
-}
-
-
-
-
-
-/*****************************************************************************
- * colorDistance
- *
- * Treat two color values as 3D vectors of the form <r,g,b>.
- * Compute the scalar size of the difference between the two vectors.
- * *****************************************************************************/
-double colorDistance (int col1, int col2)
-{
-  double distance;
-  int col1r, col1g, col1b;
-  int col2r, col2g, col2b;
-
-  col1r = getr (col1);
-  col1g = getg (col1);
-  col1b = getb (col1);
-  col2r = getr (col2);
-  col2g = getg (col2);
-  col2b = getb (col2);
-
-  // Treat the colour-cube as a space
-  distance = vector_length_f ((float)(col1r - col2r), (float)(col1g - col2g), (float)(col1b - col2b));
-  
-  return (distance);
-}
-
-
-
-
-void Clear_Weapons()
-{
-    memset(weapon, 0, WEAPONS * sizeof(WEAPON));
-    memset(naturals, 0, NATURALS * sizeof(WEAPON));
-    memset(item, 0, ITEMS * sizeof(ITEM));   
-
-}
-
-
-
-int Display_Tank_Bitmap(ENVIRONMENT *env, int xpos, int ypos, void *image_number)
-{
-    double *temp_number = (double *) image_number;
-    int my_number = (int) *temp_number;
-    int use_tank_bitmap, use_turret_bitmap;
-    BITMAP *dest = env->db;
-    GLOBALDATA *global = env->_global;
-
-    switch (my_number)
-    {
-         case CLASSIC_TANK:
-          use_tank_bitmap = 8;
-          use_turret_bitmap = 1;
-          break;
-        case BIGGREY_TANK:
-          use_tank_bitmap = 9;
-          use_turret_bitmap = 2;
-          break;
-        case T34_TANK:
-          use_tank_bitmap = 10;
-          use_turret_bitmap = 3;
-          break;
-        case HEAVY_TANK:
-          use_tank_bitmap = 11;
-          use_turret_bitmap = 4;
-          break;
-        case FUTURE_TANK:
-          use_tank_bitmap = 12;
-          use_turret_bitmap = 5;
-          break;
-        case UFO_TANK:
-          use_tank_bitmap = 13;
-          use_turret_bitmap = 6;
-          break;
-        case SPIDER_TANK:
-          use_tank_bitmap = 14;
-          use_turret_bitmap = 7;
-          break;
-        case BIGFOOT_TANK:
-          use_tank_bitmap = 15;
-          use_turret_bitmap = 8;
-          break;
-        case MINI_TANK:
-          use_tank_bitmap = 16;
-          use_turret_bitmap = 8;
-          break;
-        default:
-          use_tank_bitmap = 0;
-          use_turret_bitmap = 0;
-          break;
-
-    }
-
-    draw_sprite(dest, global->tank[use_tank_bitmap], xpos, ypos);
-    draw_sprite(dest, global->tankgun[use_turret_bitmap], xpos, ypos - TANKHEIGHT + 5);
-    
-    return TRUE;
-
-
+	char** my_list;
+	struct dirent* one_file;
+	int32_t file_count = 0;
+	DIR* game_dir;
+
+	my_list = (char**)calloc(256, sizeof(char*));
+	if (! my_list)
+		return nullptr;
+
+
+	game_dir = opendir(env.configDir);
+	if (! game_dir) {
+		free(my_list);
+		return nullptr;
+	}
+
+	one_file = readdir(game_dir);
+	while ( (one_file) && (file_count < 256) ) {
+		// check to see if this is a save game file
+#ifdef ATANKS_IS_LINUX
+		if ( strcasestr( one_file->d_name, ".bmp" ) ) {
+#else
+		if ( (strstr(one_file->d_name, ".bmp"))
+		  || (strstr(one_file->d_name, ".BMP")) ) {
+#endif // ATANKS_IS_LINUX
+			size_t nLen = strlen(env.configDir)
+			            + strlen(one_file->d_name) + 16;
+			my_list[file_count] = (char*)calloc(nLen + 1, sizeof(char) );
+			if ( my_list[file_count] ) {
+				snprintf(my_list[file_count], nLen, "%s/%s", env.configDir, one_file->d_name);
+				file_count++;
+			}
+		}
+
+		one_file = readdir(game_dir);
+	}
+
+	closedir(game_dir);
+	*bitmaps_found = file_count;
+	if (file_count < 1) {
+		free(my_list);
+		my_list = nullptr;
+	}
+
+	return my_list;
 }
 
diff --git a/src/files.h b/src/files.h
index 5fbd485..a71dd23 100644
--- a/src/files.h
+++ b/src/files.h
@@ -2,89 +2,48 @@
 #define FILE_HANDLING_HEADER_
 
 #define MAX_CONFIG_LINE 128
-#define MAX_INSULT_LINE 256
+#define MAX_INSULAND_LINE 256
 
-#define NO_STAGE 0
-#define GLOBAL_STAGE 1
-#define ENVIRONMENT_STAGE 2
-#define PLAYER_STAGE 3
+#include "debug.h"
+
+#ifndef HAS_DIRENT
+#  if defined(ATANKS_IS_MSVC)
+#    include "extern/dirent.h"
+#  else
+#    include <dirent.h>
+#  endif // Linux
+#  define HAS_DIRENT 1
+#endif //HAS_DIRENT
 
-#include <dirent.h>
 #include "globaldata.h"
 #include "environment.h"
 #include "text.h"
 
+/* Global path buffer */
+extern char path_buf[PATH_MAX + 1];
 
-int Save_Game(GLOBALDATA *global, ENVIRONMENT *env);
-int Load_Game(GLOBALDATA *global, ENVIRONMENT *env); 
-int Check_For_Saved_Game(GLOBALDATA *global);
-/*
-Copy the atanks config file from HOME_DIR to
-HOME_DIR/.atanks
-*/
-int Copy_Config_File(GLOBALDATA *global);
-
-// Make sure there is a music folder in .atanks
-int Create_Music_Folder(GLOBALDATA *global);
-
-
-void renderTextLines (GLOBALDATA *global, ENVIRONMENT *env,
-                      TEXTBLOCK *lines, int scrollOffset,
-                      const FONT* fnt, const int spacing );
-
-void scrollTextList (GLOBALDATA *global, ENVIRONMENT *env,
-                     TEXTBLOCK *lines);
 
-int draw_circlesBG (GLOBALDATA *global, BITMAP *dest, int x, int y, int width, int height, bool image);
+bool Save_Game();
+bool Load_Game();
+bool Check_For_Saved_Game();
+bool Copy_Config_File();
 
-void drawMenuBackground (GLOBALDATA *global, ENVIRONMENT *env, int itemType, int tOffset, int numItems);
 
+// Make sure there is a music folder in .atanks
+bool Create_Music_Folder();
+void scrollTextList(TEXTBLOCK* lines);
 void flush_inputs();
+bool Load_Weapons_Text();
 
-int Load_Weapons_Text(GLOBALDATA *global);
-
-// char *Get_Random_Quote(GLOBALDATA *global);
 
 #ifdef MACOSX
-int Filter_File( struct dirent *my_file );
+  int Filter_File( struct dirent *my_file );
 #else
-int Filter_File( const struct dirent *my_file );
+  int Filter_File( const struct dirent *my_file );
 #endif
 
-struct dirent ** Find_Saved_Games(GLOBALDATA *global, int *num_files_found);
-
-
-
-char ** Find_Bitmaps(GLOBALDATA *global, int *bitmaps_found);
-
-BITMAP *create_gradient_strip (const gradient *gradient, int length);
-
-int gradientColorPoint (const gradient *grad, double length, double line);
-
-double colorDistance(int col1, int col2);
-
-
-// This function removes all weapon, natural and item data.
-// Should be called before re-loading weapon file.
-void Clear_Weapons();
-
-
-// Draw a tank bitmap on the screen at the given location'
-int Display_Tank_Bitmap(ENVIRONMENT *env, int xpos, int ypos, void *image_number);
-
-// cause natural events to happen
-void doNaturals(GLOBALDATA *global, ENVIRONMENT *env);
-
-// give people the chance to buy items
-bool buystuff (GLOBALDATA *global, ENVIRONMENT *env);
-
-// display the bar at the top of the game screen
-void drawTopBar(GLOBALDATA *global, ENVIRONMENT *env, BITMAP *dest);
-
-int slideLand(GLOBALDATA *global, ENVIRONMENT *env);
-
-void set_level_settings(GLOBALDATA *global, ENVIRONMENT *env);
+dirent** Find_Saved_Games(uint32_t &num_files_found);
 
-void showRoundEndScoresAt (GLOBALDATA *global, ENVIRONMENT *env, BITMAP *bitmap, int x, int y, int winner);
+char** Find_Bitmaps(int32_t* bitmaps_found);
 
 #endif
diff --git a/src/floattext.cpp b/src/floattext.cpp
index 2c15452..fcdffdd 100644
--- a/src/floattext.cpp
+++ b/src/floattext.cpp
@@ -1,268 +1,389 @@
 #include "floattext.h"
 
-FLOATTEXT::FLOATTEXT (GLOBALDATA *global, ENVIRONMENT *env, char *text, int xpos, int ypos, int color, alignType alignment)
+FLOATTEXT::FLOATTEXT (const char* text_, int32_t xpos, int32_t ypos,
+                      double xv_, double yv_, int32_t color_,
+                      alignType alignment, eTextSway sway_type,
+                      int32_t max_age) :
+	VIRTUAL_OBJECT(),
+	color(color_),
+	pos_x(xpos),
+	pos_y(ypos)
 {
-      initialise ();
-      setEnvironment (env);
-      _text = NULL;
-      x = (double)xpos;
-      y = (double)ypos;
-      _current.x = (int)x;
-      _current.y = (int)y;
-      _current.w = 0;
-      _current.h = 0;
-      set_pos (xpos, ypos);
-      if (text)
-        set_text (text);
-      else
-        set_text(NULL);
-      set_color (color);
-      _align = alignment;
-      _global = global;
-      _halfColor = color;
-      sway = NO_SWAY;
-      original_x = xpos;
-      delta_x = 0;
+	int32_t sky_col = TURQUOISE;
+	if ( (pos_x > -1)              && (pos_y > MENUHEIGHT)
+	  && (pos_x < env.screenWidth) && (pos_y < env.screenWidth) ) {
+		sky_col = getpixel(env.sky, pos_x, pos_y - MENUHEIGHT);
+	}
+
+	halfColor = GetShadeColor(color, true, sky_col);
+	align     = alignment;
+	maxAge    = max_age;
+
+	if (text_)
+		set_text(text_);
+
+	// The font and thus its height is fixed:
+	dim_cur.h = env.fontHeight + (env.fontHeight % 2);
+
+	x         = xpos;
+	y         = ypos;
+	dim_cur.x = ROUND(pos_x);
+	dim_cur.y = ROUND(pos_y);
+	set_sway_type(sway_type);
+	set_speed(xv_, yv_);
+
+	// Add to the chain:
+	global.addObject(this);
 }
 
 
 FLOATTEXT::~FLOATTEXT()
 {
-      if (_env)
-        {
-          /* To be sure to really remove the full text (might fail due to font differences),
-             we "enlarge" the text by 25%: */
-          if (_current.w)
-            {
-              _current.x -= (int)((double)_current.w * 1.125);
-              _current.w  = (int)((double)_current.w * 1.250);
-            }
-          if (_current.h)
-            {
-              _current.y -= (int)((double)_current.h * 1.125);
-              _current.h  = (int)((double)_current.h * 1.250);
-            }
-
-          switch (_align)
-            {
-            case LEFT:
-              _env->make_bgupdate (_current.x, _current.y, _current.w, _current.h);
-              _env->make_bgupdate (_old.x, _old.y, _old.w, _old.h);
-              break;
-            case RIGHT:
-              _env->make_bgupdate (_current.x - _current.w, _current.y - _current.h, _current.w, _current.h);
-              _env->make_bgupdate (_old.x - _old.w, _old.y - _old.h, _old.w, _old.h);
-              break;
-            case CENTRE:
-              _env->make_bgupdate (_current.x - (_current.w / 2), _current.y - (_current.h / 2), _current.w + 2, _current.h + 2);
-              _env->make_bgupdate (_old.x - (_old.w / 2), _old.y - (_old.h / 2), _old.w + 2, _old.h + 2);
-              break;
-            default:
-              _env->make_bgupdate (_current.x - _current.w, _current.y - _current.h, 2 * _current.w, 2 * _current.h);
-              _env->make_bgupdate (_old.x - _old.w, _old.y - _old.h, 2 * _old.w, 2 * _old.h);
-            }
-          _env->removeObject (this);
-        }
-      _global = NULL;
-      _env    = NULL;
-      if (_text)
-      {
-         free(_text);
-         _text = NULL;
-      }
+	requireUpdate ();
+	this->update();
+
+	// Only do the final update if the dimensions have been set
+	if ( dim_cur.w > 0 ) {
+		// Update current position
+		int32_t left = LEFT  == align ? dim_cur.x
+					 : RIGHT == align ? dim_cur.x -  dim_cur.w
+					 :                  dim_cur.x - (dim_cur.w / 2);
+		int32_t top  = LEFT  == align ? dim_cur.y
+					 : RIGHT == align ? dim_cur.y -  dim_cur.h
+					 :                  dim_cur.y - (dim_cur.h / 2);
+		int32_t right  = std::min(env.screenWidth, left + dim_cur.w + 1);
+		int32_t bottom = std::min(env.screenHeight, top + dim_cur.h + 1);
+
+		global.make_bgupdate(left, top, right - left, bottom - top);
+
+		// Update previous position
+		left = LEFT  == align ? dim_old.x
+			 : RIGHT == align ? dim_old.x -  dim_old.w
+			 :                  dim_old.x - (dim_old.w / 2);
+		top  = LEFT  == align ? dim_old.y
+			 : RIGHT == align ? dim_old.y -  dim_old.h
+			 :                  dim_old.y - (dim_old.h / 2);
+		right  = std::min(env.screenWidth, left + dim_old.w + 1);
+		bottom = std::min(env.screenHeight, top + dim_old.h + 1);
+
+		global.make_bgupdate(left, top, right - left, bottom - top);
+	}
+
+	// Free allocated text
+	if (text) {
+		free(text);
+		text = nullptr;
+	}
+
+	// Take out of the chain:
+	global.removeObject(this);
 }
 
 
-int FLOATTEXT::applyPhysics()
+void FLOATTEXT::applyPhysics()
 {
-      VIRTUAL_OBJECT::applyPhysics ();
-
-      if ( (!delta_x) && (sway) )
-         delta_x = -1;
+	// Opt out early if there is no text to be drawn.
+	if ( (nullptr == text) || (dim_cur.w < 1) )
+		return;
+
+	if (TS_HORIZONTAL == sway) {
+		double x_dist = pos_x - x;
+		double rel_xv = static_cast<double>(sway - std::abs(x_dist))
+		              / static_cast<double>(sway) * SIGNd(x_dist); // [-1:+1]
+		if (std::abs(rel_xv) < 0.15)
+			// Only reverse to not stop it.
+			xv *= -1.;
+		else if (SIGN(xv) == SIGN(rel_xv))
+			xv = rel_xv;
+		else
+			xv = -1. * rel_xv;
+	} else if (TS_VERTICAL == sway) {
+		double y_dist = pos_y - y;
+		double rel_yv = static_cast<double>(sway - std::abs(y_dist))
+		              / static_cast<double>(sway) * SIGNd(y_dist); // [-1:+1]
+		if (std::abs(rel_yv) < 0.33)
+			// Only reverse to not stop it.
+			yv *= -1.;
+		else if (SIGN(yv) == SIGN(rel_yv))
+			yv = rel_yv;
+		else
+			yv = -1. * rel_yv;
+	}
+	pos_x += xv;
+	pos_y += yv;
+
+	dim_cur.x = ROUND(pos_x);
+	dim_cur.y = ROUND(pos_y);
+
+	requireUpdate();
+
+	if ( (maxAge != -1) && (++age > maxAge) )
+		destroy = true;
+}
 
-      x += delta_x;
-      if (x < original_x - sway)
-         delta_x = -delta_x;
-      else if (x > original_x + sway)
-         delta_x = -delta_x;
 
-      set_pos ((int)x, (int)y);
+/// Little Helper, move it somewhere else if it makes sense to be used elsewhere
+#define SAFE_MAKECOL(r_, g_, b_) makecol( \
+	r_ < 0 ? 0 : r_ > 255 ? 255 : r_, \
+	g_ < 0 ? 0 : g_ > 255 ? 255 : g_, \
+	b_ < 0 ? 0 : b_ > 255 ? 255 : b_)
 
-      age++;
-      if ( (maxAge != -1) && (age > maxAge) )
-        destroy = TRUE;
 
-      return (0);
+void FLOATTEXT::draw()
+{
+	// Opt out early if there is no text to be drawn.
+	if ( (nullptr == text) || !dim_cur.w)
+		return;
+
+	// If the width is not known, yet, determine it
+	if (1 > dim_cur.w) {
+		dim_cur.w = text_length(font, text) + 3;
+		dim_cur.w += dim_cur.w % 2;
+	}
+
+	// Current position according to alignment:
+	int32_t left   = LEFT  == align ? dim_cur.x
+	               : RIGHT == align ? dim_cur.x -  dim_cur.w
+	               :                  dim_cur.x - (dim_cur.w / 2);
+	int32_t top    = LEFT  == align ? dim_cur.y
+	               : RIGHT == align ? dim_cur.y -  dim_cur.h
+	               :                  dim_cur.y - (dim_cur.h / 2);
+
+	double  shadeFade = 0.75;
+	int32_t frontCol  = color;
+	int32_t shadeCol  = halfColor;
+	int32_t backCol   = color;
+
+	// If either shadowed or fading text is enabled, a background
+	// average colour is needed.
+	if ( (env.shadowedText || env.fadingText)
+	  && !global.skippingComputerPlay) {
+		backCol = global.get_avg_bgcolor(left, top, left + dim_cur.w,
+		                                 top + dim_cur.h, xv, yv);
+
+		// If fading text is activated, the front colour must be calculated as well
+		if ( env.fadingText
+		  && (maxAge > 0)
+		  && (age >= (maxAge / 2)) ) {
+			double calcMax   = maxAge / 2;
+			double calcAge   = age - calcMax;
+			double frontFade = 1.0 - (calcAge / calcMax);
+
+			shadeFade -= 0.75 * (calcAge / calcMax);
+
+			if (frontFade < 0.) frontFade = 0.;
+			if (shadeFade < 0.) shadeFade = 0.;
+
+			double backFade  = 1.0 - frontFade;
+			if (backFade < 0.) backFade = 0.;
+
+			int32_t r = ROUND( (getr(frontCol) * frontFade) + (getr(backCol) * backFade) );
+			int32_t g = ROUND( (getg(frontCol) * frontFade) + (getg(backCol) * backFade) );
+			int32_t b = ROUND( (getb(frontCol) * frontFade) + (getb(backCol) * backFade) );
+			frontCol = SAFE_MAKECOL(r, g, b);
+		} // end of calculating fading values
+
+		// The now current values must be applied to the shadow colour if needed
+		if (env.shadowedText) {
+			double backFade = 1.0 - shadeFade;
+
+			if (backFade < 0.) backFade = 0.;
+
+			int32_t r = ROUND( (getr(shadeCol) * shadeFade) + (getr(backCol) * backFade) );
+			int32_t g = ROUND( (getg(shadeCol) * shadeFade) + (getg(backCol) * backFade) );
+			int32_t b = ROUND( (getb(shadeCol) * shadeFade) + (getb(backCol) * backFade) );
+
+			shadeCol = SAFE_MAKECOL(r, g, b);
+		} // End of calculating shadow values
+	} // End of fading / shadow preparations
+
+	// Eventually print out the text:
+	if (env.shadowedText && !global.skippingComputerPlay)
+		textout_ex (global.canvas, font, text, left + 1, top + 1, shadeCol, -1);
+	textout_ex (global.canvas, font, text, left, top, frontCol, -1);
 }
 
 
-void FLOATTEXT::setEnvironment(ENVIRONMENT *env)
+void FLOATTEXT::newRound()
 {
-      if (!_env || (_env != env))
-        {
-          _env = env;
-          _index = _env->addObject (this);
-        }
-}     
-
+	if (maxAge > 0)
+		age = maxAge + 1;
+}
 
 
-void FLOATTEXT::draw(BITMAP *dest)
+// Reset movement to begin neutrally if the text is swaying
+void FLOATTEXT::reset_sway()
 {
-      // If there is no text then we have nothing to draw
-      if (! _text)
-         return;
-
-      if (_current.w)
-        {
-          double dFrontFade		=	1.0;
-          double dShadFade		= 0.75;
-          int iFrontCol				=	_color;
-          int iShadCol				=	_halfColor;
-          int iBackCol				=	_color;
-
-          // get Background color:
-          if (  (_env->dShadowedText > 0.0) || (_env->dFadingText > 0.0)  )
-          {
-            switch (_align)
-              {
-              case LEFT:
-                iBackCol = _env->getAvgBgColor(	_current.x,								_current.y,
-                                                _current.x + _current.w,	_current.y + _current.h,
-                                                xv,												yv);
-                break;
-              case RIGHT:
-                iBackCol = _env->getAvgBgColor(	_current.x - _current.w,	_current.y - _current.h,
-                                                _current.x,								_current.y,
-                                                xv,												yv);
-                break;
-              case CENTRE:
-                iBackCol = _env->getAvgBgColor(	_current.x - (_current.w / 2),		_current.y - (_current.h / 2),
-                                                _current.x + (_current.w / 2) + 2,_current.y + (_current.h / 2) + 2,
-                                                xv,																yv);
-                break;
-              }
-          }
-          if ((maxAge > 0) && (age >= (maxAge / 2)) && (_env->dFadingText > 0.0))
-            {
-              double dCalcAge = (double)age - (double)(maxAge / 2);
-              double dCalcMax = (double)(maxAge / 2);
-              dFrontFade	= 1.0 - (dCalcAge / dCalcMax);
-              dShadFade		=	0.75 - (0.75 * (dCalcAge / dCalcMax));
-              if (dFrontFade	< 0.0) dFrontFade		= 0.0;
-              if (dFrontFade	> 1.0) dFrontFade		= 1.0;
-              if (dShadFade < 0.0) dShadFade	= 0.0;
-              if (dShadFade >	1.0) dShadFade	= 1.0;
-              iFrontCol = newmakecol((getr(iFrontCol) * dFrontFade) + (getr(iBackCol) * (1.0 - dFrontFade)),
-                                  (getg(iFrontCol) * dFrontFade) + (getg(iBackCol) * (1.0 - dFrontFade)),
-                                  (getb(iFrontCol) * dFrontFade) + (getb(iBackCol) * (1.0 - dFrontFade)));
-            }
-          if (_env->dShadowedText > 0.0)
-            iShadCol = newmakecol(	(getr(iShadCol) * dShadFade) + (getr(iBackCol) * (1.0 - dShadFade)),
-                                (getg(iShadCol) * dShadFade) + (getg(iBackCol) * (1.0 - dShadFade)),
-                                (getb(iShadCol) * dShadFade) + (getb(iBackCol) * (1.0 - dShadFade)));
-          if (_align == LEFT)
-            {
-              if (_env->dShadowedText > 0.0)
-                textout_ex (dest, font, _text,
-                            _current.x + 1, _current.y + 1, iShadCol, -1);
-              textout_ex (dest, font, _text,
-                          _current.x, _current.y, iFrontCol, -1);
-            }
-          else if (_align == RIGHT)
-            {
-              if (_env->dShadowedText > 0.0)
-                textout_ex (dest, font, _text,
-                            _current.x - _current.w + 1,
-                            _current.y - _current.h + 1, iShadCol, -1);
-              textout_ex (dest, font, _text,
-                          _current.x - _current.w,
-                          _current.y - _current.h, iFrontCol, -1);
-            }
-          else
-            {
-              if (_env->dShadowedText > 0.0)
-                textout_centre_ex (dest, font, _text,
-                                   _current.x + 1, _current.y + 1, iShadCol, -1);
-              textout_centre_ex (dest, font, _text,
-                                 _current.x, _current.y, iFrontCol, -1);
-            }
-        }
+	xv    = 0.;
+	yv    = 0.;
+	pos_x = x;
+	pos_y = y;
+	if (TS_HORIZONTAL == sway) {
+		pos_x = x + ((1 + (rand() % (sway / 2))) * (rand() % 2 ? -1 : 1));
+		xv    = SIGNd(pos_x - x);
+	} else if (TS_VERTICAL == sway) {
+		pos_y = y;
+		yv    = -1.;
+	}
+	dim_cur.x = pos_x;
+	dim_cur.y = pos_y;
 }
 
 
-
-// Returns TRUE on success or FALSE on failure.
-int FLOATTEXT::set_text(char *text)
+void FLOATTEXT::set_color(int32_t color_)
 {
-   // int my_length = strlen(text);
-   int my_length;
-
-   // clean up old text
-   if (_text)
-   {
-      free(_text);
-      _text = NULL;
-   }
-   if (! text)
-      return TRUE;
-
-   my_length = strlen(text);
-   _text = (char *) calloc( my_length + 1, sizeof(char));
-   if (! _text)
-      return FALSE;
-
-   strcpy(_text, text);
-   if (my_length)
-        _current.w = text_length(font, _text) + 3;
-   else
-       _current.w = 0;
-   _current.h = text_height(font) + 15;
-   return TRUE;
+	if (color != color_)
+		color     = color_;
+
+	int32_t left   = LEFT  == align ? dim_cur.x + (dim_cur.w / 2)
+	               :                  dim_cur.x - (dim_cur.w / 2);
+	int32_t top    = LEFT  == align ? dim_cur.y + (dim_cur.h / 2)
+	               :                  dim_cur.y - (dim_cur.h / 2);
+
+	int32_t sky_col = TURQUOISE;
+	if ( (left > -1)              && (top > MENUHEIGHT)
+	  && (left < env.screenWidth) && (top < env.screenWidth) ) {
+		sky_col = getpixel(env.sky, left, top - MENUHEIGHT);
+	}
+
+	halfColor = GetShadeColor(color, true, sky_col);
 }
 
 
-void FLOATTEXT::set_color(int color)
+void FLOATTEXT::set_pos(int32_t xpos, int32_t ypos)
 {
-      int red		=	getr(color);
-      int green	=	getg(color);
-      int blue	=	getb(color);
-      int iAverage = (red + green + blue) / 3;
-
-      _color = color;
-
-      if (!(red & 0xc0) && !(green & 0xc0) && !(blue & 0xc0))
-        _halfColor = makecol(red | 0x80, green | 0x80, blue | 0x80); // Color is too dark, shadow should be bright
-      else
-        _halfColor = makecol(	red		> iAverage?(red		/ 4):(red		/ 6),
-                              green	> iAverage?(green	/ 4):(green	/ 6),
-                              blue	> iAverage?(blue	/ 4):(blue	/ 6));
+	if ( (xpos != x) || (ypos !=y) ) {
+		x     = xpos;
+		y     = ypos;
+		reset_sway();
+	}
 }
 
 
-void FLOATTEXT::newRound()
+void FLOATTEXT::set_speed(double xv_, double yv_)
 {
-   age = maxAge + 1;
+	reset_sway();
+
+	if (TS_HORIZONTAL != sway)
+		xv = xv_;
+
+	if (TS_VERTICAL != sway) {
+		if (yv_ < 0.) {
+			// avoid over-lapping text
+			double mix_it_up = ((rand() % 6) - 3.) / 10.; // [-.3;+.2]
+
+			yv = yv_ + mix_it_up;
+
+			// avoid text that does not move up
+			if (yv > -0.1)
+				yv = -0.1;
+		} else
+			yv = yv_;
+	}
 }
 
-void FLOATTEXT::set_pos(int x, int y)
+
+void FLOATTEXT::set_sway_type(eTextSway sway_type)
 {
-   _current.x = x;
-   _current.y = y;
+	if (sway_type != sway) {
+		sway = sway_type;
+		reset_sway();
+	}
 }
 
 
+void FLOATTEXT::set_text(const char* text_)
+{
+	if (text && text_ && !strcmp(text, text_))
+		return;
+
+	int new_len = text_ ? strlen(text_)  : 0;
+	int old_len = text  ? strlen(text)   : 0;
+
+	// clean up old text
+	if (text && old_len)
+		memset(text, 0, old_len * sizeof(char));
+
+	// reallocate new text
+	if (!text || (new_len > old_len)) {
+		char* new_text = (char*)realloc(text, (new_len + 1) * sizeof(char));
+		if (new_text) {
+			text = new_text;
+			memset(text, 0, (new_len + 1) * sizeof(char));
+		} else {
+			cerr << "Unable to allocate " << ( (new_len + 1) * sizeof(char));
+			cerr << " bytes for new text" << endl;
+			return;
+		}
+	}
+
+	// Copy new text if any. If not, _text is { 0x0 } already.
+	if (new_len) {
+		strncpy(text, text_, new_len);
+		dim_cur.w = -1; // draw() must determine width
+	} else
+		// Width must be reset
+		dim_cur.w = 0;
+}
+
 
-void FLOATTEXT::set_speed(double x_speed, double y_speed)
+/// @param[in] do_lighten If true, the colour is made brighter if the shade colour
+///            would be too dark to make a difference.
+/// @param[in] bg_colour If not PINK, the background colour is taken into account
+///            and the result darkened or lightened more according to @a do_lighten
+int32_t GetShadeColor(int32_t colour, bool do_lighten, int32_t bg_colour)
 {
-     double mix_it_up;       // avoid over-lapping text
-
-     mix_it_up = rand() % SPEED_RANGE;      // (0-5)
-     mix_it_up -= 3.0;                      // -3 to +2
-     mix_it_up /= 10.0;                     // -0.3 to + 0.2
-     xv = x_speed;
-     yv = y_speed + mix_it_up;
-     if ( (yv == 0.0) && (y_speed < 0.0) )
-        yv = -0.1;       // avoid text that does not move
+	int32_t r = getr(colour), g = getg(colour), b = getb(colour);
+	float   h, s, v;
+
+	if (do_lighten) {
+		// Be sure something can be done with near black colours
+		if ((r < 0x20) && (g < 0x20) && (b < 0x20)) {
+			r = 0x28;
+			g = 0x28;
+			b = 0x28;
+		}
+	}
+
+	rgb_to_hsv(r, g, b, &h, &s, &v);
+
+	if (do_lighten) {
+		if (s < 0.10) s = 0.10;
+		if (v < 0.25) v = 0.25;
+	}
+
+	int32_t rn, gn, bn;
+	double s_mod = s > 0.5 ? (s > 0.9 ? 0.33 : 0.5) : (do_lighten ? 1.75 : .75);
+	double v_mod = v > 0.5 ? (v > 0.9 ? 0.33 : 0.5) : (do_lighten ? 1.75 : .75);
+	hsv_to_rgb(h, s * s_mod * (v > 0.75 ? 0.5 : 1.),
+				        v * v_mod * (s > 0.75 ? 0.5 : 1.),
+				        &rn, &gn, &bn);
+
+	// check to see if this all fits with a possible background
+	if (bg_colour != PINK) {
+		int32_t rb = getr(bg_colour), gb = getg(bg_colour), bb = getb(bg_colour);
+		int32_t rm = (rn + r) / 2,    gm = (gn + g) / 2,    bm = (bn + b) / 2;
+
+		if (ABSDISTANCE3(rb, gb, bb, rm, gm, bm) < 0x20) {
+
+			// The middle between the colour and its shade is too near
+			// to the background. Here s_mod/v_mod can be reused:
+			rn = std::round(  static_cast<double>(rn)
+			                * (rn > r ? std::max(s_mod, v_mod)
+			                          : std::min(s_mod, v_mod)));
+			gn = std::round(  static_cast<double>(gn)
+			                * (gn > g ? std::max(s_mod, v_mod)
+			                          : std::min(s_mod, v_mod)));
+			bn = std::round(  static_cast<double>(bn)
+			                * (bn > b ? std::max(s_mod, v_mod)
+			                          : std::min(s_mod, v_mod)));
+			// Do not overflow:
+			if (rn > 255) rn = 255;
+			if (gn > 255) gn = 255;
+			if (bn > 255) bn = 255;
+		}
+	}
+
+	return makecol(rn, gn, bn);
 }
 
diff --git a/src/floattext.h b/src/floattext.h
index 027dbe6..67db9e4 100644
--- a/src/floattext.h
+++ b/src/floattext.h
@@ -21,75 +21,82 @@
  * */
 
 
-#include "virtobj.h"
 #include "main.h"
 #include "environment.h"
+#include "virtobj.h"
 
-#define newmakecol(r,g,b) makecol((int)(r),(int)(g),(int)(b))
 
-#define NO_SWAY 0
-#define NORMAL_SWAY 42
+/// @enum eTextSway
+/// @brief Type of text swaying
+enum eTextSway
+{
+	TS_NO_SWAY    =  0, //!< Static text that is moving normally
+	TS_VERTICAL   = 15, //!< Vertical "bouncing" text like tank health.
+	TS_HORIZONTAL = 22  //!< Horizontal swaying text, if turned on, used for damage and money.
+};
 
-#define SPEED_RANGE 6
 
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
 
 class FLOATTEXT: public VIRTUAL_OBJECT
-  {
-  private:
-    // empty ctor, copy-ctor and assign operator are private, so the compiler won't create implicit ones!
-    // inline const FLOATTEXT& operator= (const FLOATTEXT &sourceText) { return(sourceText); }
-
-    char *_text;
-    int	_color;
-    int _halfColor; // for shadowd text!
-    int original_x;
-    int delta_x;
-
-  public:
-    int sway;
-
-    /* --- constructor --- */
-    FLOATTEXT (GLOBALDATA *global, ENVIRONMENT *env, char *text, int xpos, int ypos, int color, alignType alignment);
-   
-    /* --- destructor --- */
-    ~FLOATTEXT ();
-    void setEnvironment(ENVIRONMENT *env);
-   
-    inline virtual void		initialise ()
-    {
-      VIRTUAL_OBJECT::initialise ();
-    }
-
-    int	applyPhysics ();
-    void draw (BITMAP *dest);
-    int set_text (char * text);
-    void set_pos (int x, int y);
-    void set_color (int color);
-    void set_speed(double x_speed, double y_speed);
-    
-
-    inline virtual int		isSubClass (int classNum)
-    {
-      if (classNum != FLOATTEXT_CLASS)
-        return (FALSE);
-      //return (VIRTUAL_OBJECT::isSubClass (classNum));
-      else
-        return (TRUE);
-    }
-
-    inline virtual int		getClass ()
-    {
-      return (FLOATTEXT_CLASS);
-    }
-
-    void newRound();
-
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit FLOATTEXT (const char* text_, int32_t xpos, int32_t ypos,
+						double xv_, double yv_, int32_t color_,
+						alignType alignment, eTextSway sway_type,
+						int32_t max_age);
+	~FLOATTEXT ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void     applyPhysics ();
+	void     draw         ();
+	void     newRound     ();
+	void     set_color    (int32_t color_);
+	void     set_pos      (int32_t xpos, int32_t ypos);
+	void     set_sway_type(eTextSway sway_type);
+	void     set_text     (const char* text_);
+
+	eClasses getClass() { return CLASS_FLOATTEXT; }
+
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void     reset_sway   ();
+	void     set_speed    (double xv_, double yv_);
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	int32_t   color     = SILVER; //!< Foreground colour
+	int32_t   halfColor = GREY;   //!< Shadow colour
+	double    pos_x     = 0.;
+	double    pos_y     = 0.;
+	eTextSway sway      = TS_NO_SWAY;
+	char*     text      = nullptr;
+};
+
+
+// This function returns a shade colour, which
+// is either brighter or darker depending on
+// the given colour and options.
+int32_t GetShadeColor(int32_t colour, bool do_lighten, int32_t bg_colour);
 
 #endif
diff --git a/src/gameloop.cpp b/src/gameloop.cpp
index 943debc..3b6314a 100644
--- a/src/gameloop.cpp
+++ b/src/gameloop.cpp
@@ -1,589 +1,2185 @@
-#include <stdio.h>
+#include <cstdio>
+#include <thread>
+#include <condition_variable>
+#include <cassert>
+
 #include "main.h"
-#include "team.h"
 #include "files.h"
 #include "satellite.h"
 #include "update.h"
 #include "network.h"
 #include "land.h"
+#include "clock.h"
 
 #include "floattext.h"
+#include "tank.h"
 #include "explosion.h"
 #include "beam.h"
 #include "missile.h"
 #include "decor.h"
 #include "teleport.h"
 #include "sky.h"
+#include "sound.h"
 
 #include "gameloop.h"
+#include "player.h"
+#include "aicore.h"
 
-#ifdef NEW_GAMELOOP
-
-int game (GLOBALDATA *global, ENVIRONMENT *env)
-{
-    int humanPlayers = 0;
-    int skippingComputerPlay = FALSE;
-    int team_won = NO_WIN;
-    int my_class;
-    int screen_update = TRUE;
-    int text_bounce = 0, text_delta = 1;
-    VIRTUAL_OBJECT *my_object;
-    TANK *my_tank, *a_tank;
-    MISSILE *missile;
-    TELEPORT *teleport;
-    DECOR *decor;
-    BEAM *beam;
-    EXPLOSION *explosion;
-    FLOATTEXT *floattext;
-    SATELLITE *satellite = NULL;
-    int count;
-    int winner = -1, done = FALSE;
-    int stuff_happening = FALSE;
-    int AI_clock = 0;
-    int fire = FALSE;
-    int roundEndCount = 0;
-    int game_speed = (int) global->frames_per_second * GAME_SPEED / 60;
-    int explosion_in_progress = FALSE;
-    
-    // adjust for Windows
-    #ifdef WIN32
-    game_speed /= 1000;
-    #endif
-
-    global->computerPlayersOnly = FALSE;
-    // clear surface
-    for (count = 0; count < global->screenWidth; count++)
-       env->done[count] = 0;
-
-    env->newRound();
-    env->Set_Wall_Colour();
-    global->bIsBoxed = env->Get_Boxed_Mode(global);
-    global->dMaxVelocity = global->Calc_Max_Velocity();
-   
-    for (count = 0; count <global->numPlayers; count++)
-        global->players[count]->newRound();
+#include "shop.h"
 
-    // clear floating text
-    for (count = 0; count < MAX_OBJECTS; count++)
-    {
-         if (env->objects[count] && (env->objects[count]->isSubClass(FLOATTEXT_CLASS)))
-        ((FLOATTEXT *)env->objects[count])->newRound();
-    }
-
-    // everyone gets to buy stuff
-    buystuff(global, env);
-    set_level_settings(global, env);
-
-    for (count = 0; count < global->numPlayers; count++)
-    {
-        global->players[count]->exitShop();
-        if ( (global->players[count]->type == HUMAN_PLAYER) ||
-             (global->players[count]->type == NETWORK_CLIENT) )
-           humanPlayers++;
-        if (global->players[count]->tank)
-        {
-              // global->players[count]->tank->newRound();
-              global->players[count]->tank->flashdamage = 0;
-              global->players[count]->tank->boost_up_shield();
-         }
-        
-    }
-
-    // set_level_settings(global, env);
-    env->stage = STAGE_AIM;
-    fi = global->updateMenu = TRUE;
-    global->window.x = 0;
-    global->window.y = 0;
-    global->window.w = global->screenWidth - 1;
-    global->window.h = global->screenHeight - 1;
+/// Forwarding for function arguments
+class ObjectUpdater;
 
-    // set wind
-    if ( (int) env->windstrength != 0)
-       env->wind = (float) (rand() % (int)env->windstrength) - (env->windstrength / 2);
-    else
-       env->wind = 0;
-    env->lastwind = env->wind;
-
-    // create satellite
-    if (env->satellite)
-        satellite = new SATELLITE(global, env);
-    if (satellite)
-       satellite->Init();
-
-    // get some mood music
-    global->background_music = global->Load_Background_Music();
-    if (global->background_music)
-       play_sample( (SAMPLE *) global->background_music, 255, 128, 1000, TRUE);
-
-    my_tank = env->order[0];
-    // init stuff complete. Get down to playing
-    while ( (! done) && (global->get_command() != GLOBAL_COMMAND_QUIT) )
-    {
-        // see how long we have been skipping
-        if (skippingComputerPlay)
-        {
-            // advance clock
-            if (global->Check_Time_Changed() )
-                AI_clock++;
-            if ( (AI_clock > MAX_AI_TIME) && (winner == -1) )
-            {
-                // in over-time, kill all tanks
-                count = 0;
-                while (count < global->numPlayers)
-                {
-                    if ( (global->players[count]) &&
-                         (global->players[count]->tank) )
-                       global->players[count]->tank->l = 0;
-                    count++;
-                }
-            }
-        }
-
-
-        global->currTank = my_tank;
-        // move through all objects, updating them
-        explosion_in_progress = FALSE;
-        count = 0;
-        while (count < MAX_OBJECTS)
-        {
-            my_object = env->objects[count];
-            if (my_object)
-            {
-            my_class = my_object->getClass();
-            if (my_class == DECOR_CLASS)
-            {
-              decor = (DECOR *) my_object;
-              decor->applyPhysics ();
-              if (decor->destroy)
-                {
-                  decor->requireUpdate ();
-                  decor->update ();
-                  delete decor;
-                }
-            }
-            else if (my_class == EXPLOSION_CLASS)
-            {
-              explosion = (EXPLOSION *) my_object;
-              if (explosion->bIsWeaponExplosion)
-                 stuff_happening = TRUE;
-              explosion->explode ();
-              explosion_in_progress = TRUE;
-              explosion->applyPhysics ();
-              if (explosion->destroy)
-                {
-                  explosion->requireUpdate ();
-                  explosion->update ();
-                  delete explosion;
-                }
-            }
-
-            else if (my_class == TELEPORT_CLASS)
-            {
-              teleport = (TELEPORT *) my_object;
-              stuff_happening = TRUE;
-              teleport->applyPhysics ();
-              if (teleport->destroy)
-                {
-                  teleport->requireUpdate ();
-                  teleport->update ();
-                  delete teleport;
-                }
-            }
-            else if (my_class == MISSILE_CLASS)
-            {
-              TANK *shooting_tank = NULL;
-              int angle_to_fire;
-
-              missile = (MISSILE *) my_object;
-              stuff_happening = TRUE;
-              env->am++;
-              missile->hitSomething = 0;
-              missile->applyPhysics ();
-              missile->triggerTest ();
-              shooting_tank = missile->Check_SDI(global);
-              if (shooting_tank)
-              {
-                  // (shooting_tank->x < missile->x) ? angle_to_fire = 135 : angle_to_fire = 225;
-                  angle_to_fire = atan2(labs(shooting_tank->y - 10 - missile->y), (missile->x - shooting_tank->x)) * 180 / 3.14159265 + 90;
-                  new BEAM(global, env, shooting_tank->x, shooting_tank->y - 10, angle_to_fire, SML_LAZER);
-                  missile->trigger();
-              }
-              if (missile->destroy)
-              {
-                  missile->requireUpdate ();
-                  missile->update ();
-                  delete missile;
-              }
-            }
-            else if (my_class == BEAM_CLASS)
-            {
-              beam = (BEAM *) my_object;
-              // As bots should not target while a laser is shot:
-              stuff_happening = TRUE;
-              beam->applyPhysics ();
-              if (beam->destroy)
-                {
-                  beam->requireUpdate ();
-                  beam->update ();
-                  delete beam;
-                }
-            }
-            else if (my_class == FLOATTEXT_CLASS)
-            {
-              floattext = (FLOATTEXT *) my_object;
-              floattext->applyPhysics ();
-              if (floattext->destroy)
-                {
-                  floattext->requireUpdate();
-                  floattext->update();
-                  delete floattext;
-                  env->make_fullUpdate(); // ...kill remaining texts!
-                }
-            }
-            }        // end of if we have an object
-
-            count++;
-        }       // end of updating envirnoment objects
+/// === Helper functions ===
+static inline bool   advance_tank        ();
+static inline void   change_wind_strength();
+static inline void   check_fps           (ObjectUpdater* upd);
+static inline void   check_overtime      (AICore &aicore);
+static inline void   check_skiptime      ();
+static inline void   clear_voices        ();
+static inline void   check_winner        ();
+static inline double colorDistance       (int32_t col1, int32_t col2);
+static inline void   delete_destroyed    (AICore &aicore);
+static inline void   do_naturals         ();
+static inline void   draw_FPS_Counter    ();
+static inline void   draw_objects        (AICore &aicore);
+static inline void   draw_eor_scoreboard (); // The [e]nd-[o]f-[r]ound score board
+static inline void   draw_mini_scoreboard(); // The ingame mini score board
+              void   draw_top_bar        ();
+static inline bool   explode_tanks       ();
+static inline void   fire_weapon         ();
+static inline void   graph_bar           (int32_t x, int32_t y, int32_t col,
+                                   int32_t actual, int32_t max);
+static inline void   graph_bar_center    (int32_t x, int32_t y, int32_t col,
+                                   int32_t actual, int32_t max);
+static inline void   init_new_round      ();
+static inline bool   manage_input        (AICore &aicore);
+static inline void   set_level_settings  (LevelCreator* lcr);
+static inline void   set_tank_settings   ();
+static inline void   update_display      ();
+static inline void   update_objects      (ObjectUpdater* upd);
 
-    if (satellite)
-       satellite->Move();
-
-    // move land
-    slideLand(global, env);
-
-    // seems like a good place to update tanks
-    for (count = 0; count < global->numPlayers; count++)
-    {
-        a_tank = global->players[count]->tank;
-        if (a_tank)
-        {
-           // see if we are doing a volly
-           if (a_tank->fire_another_shot)
-           {
-              if (! (a_tank->fire_another_shot % VOLLY_DELAY) )
-                 a_tank->activateCurrentSelection();
-              a_tank->fire_another_shot--;
-           }
-
-           if (a_tank->flashdamage)
-           {
-              if (a_tank->flashdamage > 25 || a_tank->l < 1)
-              {
-                 a_tank->damage = 0;
-                 a_tank->flashdamage = 0;
-                 a_tank->requireUpdate();
-              }
-           }
-
-           // update position
-           a_tank->pen = 0;
-           a_tank->applyPhysics();
-
-           if ( (a_tank->l <= 0) && (! explosion_in_progress) )    // dead tank
-           {
-               a_tank->explode();
-               a_tank->Give_Credit(global);
-               if (a_tank->destroy)
-               {
-                   if ( (a_tank->player) && 
-                        ( (a_tank->player->type == HUMAN_PLAYER) ||
-                          (a_tank->player->type == NETWORK_CLIENT) ) )
-                   {
-                       humanPlayers--;
-                       if ( (! humanPlayers) && (global->skipComputerPlay) )
-                           skippingComputerPlay = TRUE;
-                   }
-
-                   a_tank->Destroy();
-                   if (my_tank == a_tank)
-                     my_tank = NULL;
-                   delete a_tank;
-                   a_tank = NULL;
-               }
-           }
-          
-           // if the tank is still alive, adjust its chess-style clock
-           if ( (global->max_fire_time > 0.0) &&
-                (a_tank == my_tank) &&
-                (a_tank) && (a_tank->player->type == HUMAN_PLAYER) &&
-                (! env->stage) )
-           {
-               if (global->Check_Time_Changed() )
-               {
-                    int ran_out_of_time;
-                    ran_out_of_time = a_tank->player->Reduce_Time_Clock();
-                    if (ran_out_of_time)
-                    {
-                        a_tank->player->skip_me = true;
-                        a_tank = NULL;
-                        fire = FALSE;
-                        my_tank = env->Get_Next_Tank(&fire);
-                    }
-                    global->updateMenu = TRUE;
-               }
-           }
-                
-        }
-    }
-    
-    #ifdef NETWORK
-    // check for input from network
-    for (count = 0; count < global->numPlayers; count++)
-    {
-         if (global->players[count]->type == NETWORK_CLIENT)
-         {
-             global->players[count]->Get_Network_Command();
-             global->players[count]->Execute_Network_Command(FALSE);
-         }
-    }   
-    #endif
- 
-    // drop some naturals
-    if (! stuff_happening)
-    {
-        env->stage = STAGE_AIM;
-        doNaturals(global, env);
-        if (satellite)
-            satellite->Shoot();
-    }
-
-    // draw top bar
-    if (global->updateMenu)
-    {
-         set_clip_rect(env->db, 0, 0, global->screenWidth - 1,
-                       MENUHEIGHT - 1);
-         drawTopBar(global, env, env->db);
-         global->updateMenu = FALSE;
-    }
-
-
-        set_clip_rect (env->db, 0, MENUHEIGHT, 
-                       (global->screenWidth-1), (global->screenHeight-1));
-        if (screen_update)
-        {
-          blit (env->sky, env->db, global->window.x, global->window.y - MENUHEIGHT, global->window.x, global->window.y, (global->window.w - global->window.x) + 1, (global->window.h - global->window.y) + 1);
-          masked_blit (env->terrain, env->db, global->window.x, global->window.y, global->window.x, global->window.y, (global->window.w - global->window.x) + 1, (global->window.h - global->window.y) + 2);
-          // drawTopBar(global, env, env->db);
-          int iLeft = 0;
-          int iRight = global->screenWidth - 1;
-          int iTop = MENUHEIGHT;
-          int iBottom = global->screenHeight - 1;
-          set_clip_rect (env->db, 0, 0, iRight, iBottom);
-          vline(env->db, iLeft, iTop, iBottom, env->wallColour);    // Left edge
-          vline(env->db, iLeft + 1, iTop, iBottom, env->wallColour);    // Left edge
-          vline(env->db, iRight, iTop, iBottom, env->wallColour);   // right edge
-          vline(env->db, iRight - 1, iTop, iBottom, env->wallColour);   // right edge
-          hline(env->db, iLeft, iBottom, iRight, env->wallColour);// bottom edge
-          if (global->bIsBoxed)
-              hline(env->db, iLeft, iTop, iRight, env->wallColour);// top edge
-
-          env->make_update(0, 0, global->screenWidth, global->screenHeight);
-        }
-        else
-        {
-          env->replaceCanvas ();
-          screen_update = TRUE;
-        }
-        for (count = 0; count < global->numPlayers; count++)
-        {
-           if ( (global->players[count]) && (global->players[count]->tank) )
-           {
-              global->players[count]->tank->framelyAccounting();
-              if ( my_tank == global->players[count]->tank )
-              {
-                 text_bounce += text_delta;
-                 global->players[count]->tank->drawTank(env->db, text_bounce / 4);
-                 if ( (text_bounce > MAX_TEXT_BOUNCE) ||
-                      (text_bounce < 1) )
-                    text_delta = -text_delta;
-              }
-                 
-              else
-                 global->players[count]->tank->drawTank(env->db, 0);
-              global->players[count]->tank->update();
-           }
-        }
-
-
-    // draw all this cool stuff
-    count = 0;
-    while (count < MAX_OBJECTS)
-    {
-       my_object = env->objects[count];
-       if (my_object)
-       {
-         my_class = my_object->getClass();
-         if (my_class == MISSILE_CLASS)
-         {
-           missile = (MISSILE *) my_object;
-           missile->draw (env->db);
-           missile->update ();
-         }
-        else if (my_class == BEAM_CLASS)
-        {
-          beam = (BEAM *) my_object;
-          beam->draw (env->db);
-          beam->update ();
-        }
-        else if (my_class == EXPLOSION_CLASS)
-        {
-          explosion = (EXPLOSION *) my_object;
-          explosion->draw (env->db);
-          explosion->update ();
-        }
-        else if (my_class == TELEPORT_CLASS)
-        {
-           teleport = (TELEPORT *) my_object;
-           if (teleport->object)
-             teleport->draw (env->db);
-           teleport->update ();
-        }
-        else if (my_class == DECOR_CLASS)
-         {
-           decor = (DECOR *) my_object;
-           decor->draw (env->db);
-           decor->update ();
-         }
-         else if (my_class == FLOATTEXT_CLASS)
-         {
-           floattext = (FLOATTEXT *) my_object;
-           floattext->draw (env->db);
-           floattext->requireUpdate ();
-           floattext->update ();
-         }
-        }        // end of if we have an object
-        count++;
-    }       // end of while drawing objects
-    if (satellite)
-       satellite->Draw(env->db);
-    env->do_updates();
-
-     // it is possible our tank is dead, make sure we have one
-     if (! my_tank)
-     {
-        my_tank = env->Get_Next_Tank(&fire);
-        fire = FALSE;
-     }
-
-     // get user input
-     if ( (my_tank) && (env->stage < STAGE_ENDGAME) )
-     {
-        PLAYER *my_player;
-        int control_result;
-
-        global->updateMenu = FALSE;
-        my_player = my_tank->player;
-        // my_tank->reactivate_shield();
-        control_result = my_player->controlTank();
-        if (control_result == CONTROL_QUIT)
-           done = TRUE;
-        else if (control_result == CONTROL_FIRE)
-        {
-           my_tank = env->Get_Next_Tank(&fire);
-           if (global->turntype != TURN_SIMUL)
-              fire = TRUE;
-        }
-        else if ( (control_result == CONTROL_SKIP) && (! humanPlayers) )
-            skippingComputerPlay = TRUE;
-
-        // if ( (fire) && (env->stage == STAGE_AIM) )
-        if (fire)
-        {
-           env->stage = STAGE_FIRE;
-           if (my_tank)
-                my_tank->reactivate_shield();
-           doLaunch(global, env);
-           fire = FALSE;
-           // screen_update = TRUE;
-        }
-        // else
-           // screen_update = FALSE;
-
-        screen_update = FALSE;
-        if (control_result)
-           global->updateMenu = TRUE;
-     }     // end of controling my tank
-
-     if ( (! skippingComputerPlay) && (env->stage != STAGE_ENDGAME) )
-     {
-         #ifdef LINUX
-         usleep(game_speed);
-         #endif
-         #ifdef MACOSX
-         usleep(game_speed);
-         #endif
-         #ifdef WIN32
-         Sleep(game_speed);
-         #endif
-     }
-         stuff_happening = FALSE;
-         // check for winner
-         if (env->stage < STAGE_ENDGAME)
-         {
-            team_won = Team_Won(global);
-            if (team_won == NO_WIN)
-               winner = global->Check_For_Winner();
-
-            if ( (winner >= 0) || (winner == -2) || (team_won != NO_WIN) )
-               env->stage = STAGE_ENDGAME;
-         }
-
-         if (env->stage == STAGE_ENDGAME)
-         {
-             roundEndCount++;
-             if (roundEndCount >= WAIT_AT_END_OF_ROUND)
-                done = TRUE;
-             screen_update = FALSE;
-         }
-         if (global->close_button_pressed)
-             done = TRUE;
-    }    // end of game loop
-
-    // credit winners
-    if (team_won != NO_WIN)
-       winner = team_won;
-    global->Credit_Winners(winner);
-
-    // display winning results
-    while ( keypressed() )
-         readkey();     // clear the buffer
-
-    // check to see if we have a winner or we just bailed out early
-    if ( (winner != -1) && (! global->demo_mode) && 
-         (! global->close_button_pressed) )
-    {
-          showRoundEndScoresAt(global, env, env->db, global->screenWidth / 2,
-                               global->screenHeight / 2, winner);
-          env->do_updates();
-
-          while ( (! keypressed() ) && (! global->demo_mode) )
-              LINUX_SLEEP;
-          readkey();
-    }
-    // else
-    // {
-        global->window.x = global->window.y = 0;
-        global->window.w = global->screenWidth - 1;
-        global->window.h = global->screenHeight - 1;
-        set_clip_rect(env->db, 0, 0, global->window.w, global->window.h);
-    // }
+
+/// === Static helper values ===
+static int32_t AI_time_change  = 0;
+static TANK*   curr_tank       = nullptr;
+static bool    death_substitute= false;
+static bool    fire            = false;
+static int32_t FPS_counter     = 0;
+static int32_t FPS_last        = 0;
+static int32_t FPS_pos         = 0;
+static int32_t game_us_needed  = 0;
+volatile
+static abool_t has_action      = ATOMIC_VAR_INIT(false);
+volatile
+static abool_t has_deco        = ATOMIC_VAR_INIT(false);
+volatile
+static abool_t has_explosion   = ATOMIC_VAR_INIT(false);
+static int32_t human_players   = 0;
+static TANK*   next_tank       = nullptr;
+static bool    order_wrapped   = false;
+static int32_t score_name_pos  = 0;
+static int32_t score_money_pos = 0;
+volatile
+static bool    second_passed   = false;
+volatile
+static bool    show_frame      = true;
+static int32_t skip_health     = 0;
+static int32_t smkIdx          = -1;
+static bool    update_screen   = true;
+static int32_t us_per_frame    = 0;
+volatile
+static int32_t winner          = WINNER_NO_WIN;
+
+
+// Note: Here mutexes must be used, condition_variable
+// can not use anything else.
+std::mutex              updMutex;
+std::condition_variable updCondition;
+
+
+/// Helper Class to multi-thread object updating
+class ObjectUpdater
+{
+	eClasses class_    = CLASS_COUNT;
+	abool_t  doExit;
+	abool_t  doStart;
+	abool_t  isDone;
+	abool_t  isExited;
+	int32_t  force_age = 0; // Only used for CLASS_DECOR_SMOKE to force more aging
+
+public:
+
+	explicit ObjectUpdater() :
+		doExit  (ATOMIC_VAR_INIT(false)),
+		doStart (ATOMIC_VAR_INIT(false)),
+		isDone  (ATOMIC_VAR_INIT(false)),
+		isExited(ATOMIC_VAR_INIT(false))
+	{ /* nothing to see here */ }
+
+	/// @brief the main thread handler.
+	void operator()()
+	{
+		vobj_t* next_obj = nullptr;
+		vobj_t* obj      = nullptr;
+		TANK*   tmp_tank = nullptr;
+
+
+		// The thread is valid until someone tells it to exit
+		while (!doExit.load()) {
+
+			// Sleep until called
+			std::unique_lock<std::mutex> updLock(updMutex);
+			updCondition.wait(updLock, [this]{
+				return (doStart.load(ATOMIC_READ) || doExit.load(ATOMIC_READ));
+			} );
+
+			// Early quit if this is a call to do so:
+			if (doExit.load())
+				continue;
+
+			// If this is the TANK class, yield once if
+			// there is no known explosion, yet.
+			if ((CLASS_TANK == class_) && (false == has_explosion.load()) )
+				// Note: No argument to load(), use the most strict default!
+				std::this_thread::yield();
+
+
+			// Okay, do the updating for this class:
+			global.getHeadOfClass(static_cast<eClasses>(class_), &obj);
+
+			// If this is the floating text class, lock it, or AI
+			// feedback might lead to data races.
+			if (CLASS_FLOATTEXT == class_)
+				global.lockClass(class_);
+
+			while (obj) {
+
+				// Explosions must be known at once:
+				if ( (false == has_explosion.load(ATOMIC_READ))
+				  && (CLASS_EXPLOSION == class_) ) {
+					has_explosion.store(true);
+					has_action.store(true);
+				}
+
+				// Make sure we know when stuff is happening!
+				if ( (false == has_action.load(ATOMIC_READ))
+				  && ( ( ( (CLASS_BEAM      == class_)
+				        || (CLASS_MISSILE   == class_) )
+				      && static_cast<PHYSICAL_OBJECT*>(obj)->isWeapon() )
+				    || (CLASS_TELEPORT  == class_) ) )
+					has_action.store(true);
+
+				obj->getNext(&next_obj);
+
+				// Trigger Explosion progress
+				if (CLASS_EXPLOSION == class_)
+					static_cast<EXPLOSION*>(obj)->explode();
+
+				// Apply forced smoke ageing
+				if ( (CLASS_DECOR_SMOKE == class_) && force_age)
+					static_cast<DECOR*>(obj)->force_aging(force_age);
+
+				// Do tank special handling
+				if (CLASS_TANK == class_) {
+					tmp_tank = static_cast<TANK*>(obj);
+
+					if (!tmp_tank->destroy) {
+						// Activate next volley shot if applicable
+						if (tmp_tank->fire_another_shot) {
+							if (! (tmp_tank->fire_another_shot % env.volley_delay) ) {
+								has_action.store(true);
+								tmp_tank->activateCurrentSelection();
+							}
+							tmp_tank->fire_another_shot--;
+						}
+
+						// Move and possibly apply pending damage
+						tmp_tank->applyPhysics();
+						tmp_tank->resetFlashDamage();
+						if (tmp_tank->isFlying())
+							has_action.store(true);
+					}
+
+
+					// If the tank is still alive, adjust its chess-style clock
+					if ( !tmp_tank->destroy
+					  && (env.maxFireTime > 0)
+					  && (tmp_tank     == curr_tank)
+					  && (HUMAN_PLAYER == tmp_tank->player->type)
+					  && (STAGE_AIM    == global.stage)
+					  && second_passed
+					  && tmp_tank->player->reduceClock() ) {
+						tmp_tank->player->skip_me = true;
+						tmp_tank  = nullptr;
+						fire      = false;
+						curr_tank = next_tank ? next_tank : global.get_next_tank(&order_wrapped);
+						next_tank = nullptr;
+						global.set_curr_tank(curr_tank);
+					}
+
+					tmp_tank = nullptr;
+				} else
+					// All others need to apply physics
+					obj->applyPhysics();
+
+				obj = next_obj;
+			} // End of looping objects of one class
+
+			// If this is the floating text class, unlock it again.
+			if (CLASS_FLOATTEXT == class_)
+				global.unlockClass(class_);
+
+			// All done
+			isDone.store( true,  ATOMIC_WRITE);
+			doStart.store(false, ATOMIC_WRITE);
+		} // End of while not exiting
+
+		isExited.store(true, ATOMIC_WRITE);
+	}
+
+	void finish     ()                 { doExit.store(true); }
+	bool hasDone    () const           { return isDone.load(ATOMIC_READ); }
+	bool hasExited  () const           { return isExited.load(ATOMIC_READ); }
+	void setClass   (eClasses aClass_) { class_ = aClass_; }
+	void setForceAge(int32_t ageing_)  { force_age = ageing_; }
+	void start      ()                 { isDone.store(false, ATOMIC_WRITE);
+	                                     doStart.store(true, ATOMIC_WRITE); }
+};
+
+
+/// The main game loop. Everything happens here.
+void game ()
+{
+	volatile
+	bool       done             = false;
+	volatile
+	int32_t    round_end_count  = 0;
+	SATELLITE* satellite        = nullptr;
+	const
+	int32_t    EndOfRoundFrames = env.frames_per_second * WAIT_AT_END_OF_ROUND;
+	AICore     aicore;
+
+	// Check whether the AI Core is in any state to do work:
+	if (!aicore.can_work()) {
+		perror("The AI core could not be initialized");
+		global.set_command(GLOBAL_COMMAND_QUIT);
+		return;
+	}
+
+	// Initialize the new round
+	init_new_round();
+
+	// Only prepare the game if the player did not close
+	// the window in the buy screen
+	if ( (global.get_command() == GLOBAL_COMMAND_QUIT)
+	  || (global.isCloseBtnPressed()) )
+		return;
+
+	// Now that everybody has done their shopping, initialize the tanks
+	set_tank_settings();
+
+	// Create the AI Core thread
+	std::thread aithread(std::ref(aicore));
+
+	// Create one updater thread per object class:
+	ObjectUpdater updater[CLASS_COUNT];
+	std::thread*  threads[CLASS_COUNT];
+
+	// Set each updater to an individual class, but note the index
+	// of the SMOKE. The smoke decoration index is used to force faster
+	// ageing when rendering a frame takes too long.
+	for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_) {
+		if (CLASS_DECOR_SMOKE == class_)
+			smkIdx = class_;
+
+		updater[class_].setClass(static_cast<eClasses>(class_));
+		threads[class_] = new std::thread(std::ref(updater[class_]));
+	}
+
+	// create satellite
+	if (env.satellite)
+		satellite = new SATELLITE();
+
+	// get some mood music
+	play_music();
+
+	// Final round preparation
+	global.AI_clock             = -1;
+	global.skippingComputerPlay = false;
+	curr_tank                   = global.order[0];
+	next_tank                   = nullptr;
+	death_substitute            = false;
+	global.set_curr_tank(curr_tank);
+
+	WIN_CLOCK_INIT
+	game_us_reset();
+
+
+	/* ==============================================================
+	 * ==== init stuff complete. Get down to playing             ====
+	 * ==============================================================
+	 */
+
+	while ( !done && (global.get_command() != GLOBAL_COMMAND_QUIT) ) {
+
+		// No action and no explosions, yet.
+		has_action.store(false);
+		has_explosion.store(false);
+
+		// For the chess-style clock and the AI clock in skipping computer
+		// play it is necessary to know when a second has passed.
+		second_passed = global.check_time_changed();
+
+
+		// Check overtime and do frame display flipping
+		if (global.skippingComputerPlay) {
+			if (winner != WINNER_DRAW)
+				check_overtime(aicore);
+
+			// End skipping play if the game is over:
+			if (global.stage >= STAGE_SCOREBOARD) {
+				global.skippingComputerPlay = false;
+				show_frame = true;
+			}
+		}
+
+
+		// free used voices if possible.
+		clear_voices();
+
+
+		// Check whether the system is good with the amount of work
+		// for the last frame:
+		check_fps(updater);
+
+
+		// Update all objects
+		update_objects(updater);
+
+
+		// Move that flying saucer
+		if (satellite)
+			satellite->move();
+
+
+		// move land
+		global.slideLand();
+
+
+		// Delete everything that was destroyed
+		delete_destroyed(aicore);
+
+
+		// Drop some naturals if applicable
+		if ( (false == has_action.load(ATOMIC_READ))
+		  && !global.skippingComputerPlay
+		  && (winner == WINNER_NO_WIN) ) {
+			do_naturals();
+
+			if (satellite)
+				satellite->shoot();
+		}
+
+		// Now update / prepare the display for drawing
+		update_display();
+
+
+		// draw top bar
+		if (global.updateMenu)
+			draw_top_bar();
+
+
+		// draw all this cool stuff
+		draw_objects(aicore);
+
+
+		if (satellite)
+			satellite->draw();
+
+
+		// If requested, show the mini scoreboard
+		if (global.showScoreBoard)
+			draw_mini_scoreboard();
+
+
+		// If wanted, an FPS Counter is shown
+		if (env.showFPS)
+			draw_FPS_Counter();
+
+		// Show the end of round scoreboard if it is needed
+		if ( !global.skippingComputerPlay
+		  && (STAGE_SCOREBOARD == global.stage)
+		  && (global.get_command() != GLOBAL_COMMAND_QUIT) )
+			draw_eor_scoreboard();
+
+
+		// Now update what has been drawn
+		if (show_frame) {
+			// Draw custom mouse cursor
+			SHOW_MOUSE(global.canvas);
+			global.do_updates();
+		}
+
+		// Let bots do their thinking and perform synchronized input reaction
+		if (curr_tank || (STAGE_SCOREBOARD > global.stage))
+			done = manage_input(aicore);
+
+
+		// Fire selected stuff (if any) and check skipping time
+		if (fire && (STAGE_AIM == global.stage)) {
+			fire_weapon();
+
+			if (global.skippingComputerPlay && order_wrapped)
+				check_skiptime();
+		}
+
+#ifdef NETWORK
+		// check for input from network
+		for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+			if (env.players[i]->type == NETWORK_CLIENT) {
+				env.players[i]->getNetCmd();
+				env.players[i]->executeNetCmd(false, &aicore);
+			}
+		}
+#endif // NETWORK
+
+
+		// Advance to next tank ?
+		if (!advance_tank()
+		  && (STAGE_ENDGAME > global.stage) )
+			// In this case at least check for exploding tanks.
+			explode_tanks();
+
+
+		// check for winner
+		if ( (false == has_explosion.load(ATOMIC_READ))
+		  && (global.stage < STAGE_ENDGAME) )
+			check_winner();
+
+
+		// manage the end of the round
+		if (global.stage == STAGE_SCOREBOARD) {
+			if ( !explode_tanks()
+			  && (false == has_explosion.load())
+			  && (false == has_action.load())
+			  && ( (++round_end_count >= EndOfRoundFrames)
+			    || (WINNER_DRAW == winner) ) ) {
+				if (false == has_deco.load(ATOMIC_READ)) {
+					done = true;
+					global.stage = STAGE_ENDGAME;
+				}
+			} else if ( has_explosion.load() )
+				// recheck for winner
+				check_winner();
+			else if ( has_action.load() )
+				round_end_count = 0;
+		}
+
+
+		// Possibly enter AI skipping mode
+		if ( !human_players && env.skipComputerPlay
+		  && !global.skippingComputerPlay
+		  && (false == has_action.load())
+		  && (global.numTanks > 1)
+		  && (STAGE_SCOREBOARD > global.stage)
+		  && (WINNER_NO_WIN == winner) ) {
+			global.skippingComputerPlay = true;
+			global.AI_clock = 0;
+		}
+
+
+		// Quit if the close button was pressed
+		if (global.isCloseBtnPressed())
+			done = true;
+	}
+
+	/* ==========================
+	 * ==== end of game loop ====
+	 * ==========================
+	 */
+
+
+	// Stop the AI thread
+	aicore.stop();
+
+	// Stop the updater threads:
+	for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_)
+		updater[class_].finish();
+	updMutex.lock();
+	updCondition.notify_all(); // <-- That's hilariously simple, isn't it?
+	updMutex.unlock();
+
+	// Wait and join all threads
+	bool has_thread = true;
+	bool has_aicore = true;
+
+	while (has_thread) {
+
+		has_thread = false;
+
+		// AI Core:
+		if (has_aicore) {
+			if (aicore.hasExited()) {
+				aithread.join();
+				has_aicore = false;
+			} else
+				has_thread = true;
+		}
+
+		// Object updaters:
+		for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_) {
+			if (threads[class_]) {
+				if (updater[class_].hasExited()) {
+					threads[class_]->join();
+					delete threads[class_];
+					threads[class_] = nullptr;
+				} else
+					has_thread = true;
+			}
+		}
+		if (has_thread)
+			std::this_thread::yield();
+	} // end of waiting for all threads to join
+
+
+	// Show end of round score board and credit winner(s)
+	if ((global.get_command() != GLOBAL_COMMAND_QUIT))
+		draw_eor_scoreboard();
+
+	// Ensure full window clipping rectangle
+	set_clip_rect(global.canvas, 0, 0, env.window.w, env.window.h);
 
     // clean up
+
     if (satellite)
        delete satellite;
 
-    // remove existing tanks etc
-    for (count = 0; count < global->numPlayers; count++)
-    {
-       if (global->players[count]->tank)
-       {
-          global->players[count]->Reclaim_Shield();
-          delete global->players[count]->tank;
-       }
-    }
+	// remove existing tanks etc
+	for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+		if (env.players[i]->tank) {
+			env.players[i]->reclaimShield();
+			delete env.players[i]->tank;
+		}
+	}
+
+	WIN_CLOCK_REMOVE
+}
+
 
-    return done;
+
+///  ==========================
+///   === Helper functions ===
+///  ==========================
+
+
+static inline bool advance_tank()
+{
+	/* The whole finding of a next tank to have their shot is needed in
+	 * two situations during the fire stage:
+	 * a) When all action has ceased, because then the turn is over.
+	 * b) The current tanks has exploded, a substitute must be found.
+	 */
+
+	if ( ( (false == has_action.load(ATOMIC_READ))
+		&& (STAGE_FIRE == global.stage) )
+	  || ( (global.stage < STAGE_SCOREBOARD)
+		&& (!curr_tank || !curr_tank->player || curr_tank->destroy) ) ) {
+
+		bool need_update = false;
+
+		if ( (STAGE_FIRE == global.stage)
+		  && !explode_tanks()
+		  && (false == has_explosion.load(ATOMIC_READ))
+		  && (false == has_action.load(ATOMIC_READ)) ) {
+			// Note: has_action is checked last, so dead tanks can
+			//       explode even if others are still falling/flying
+			//       around.
+
+			// Normal tank advancement after firing stage
+			global.stage  = STAGE_AIM;
+			order_wrapped = false;
+			change_wind_strength();
+
+			if ( !death_substitute ) {
+				if ( next_tank && !next_tank->destroy
+				  && (next_tank != curr_tank))
+					curr_tank = next_tank;
+				else
+					curr_tank = global.get_next_tank(&order_wrapped);
+			}
+
+			death_substitute = false;
+			next_tank        = nullptr;
+			need_update      = true;
+		} else if (!curr_tank || !curr_tank->player || curr_tank->destroy) {
+			// We need a death substitute
+			if (next_tank && !next_tank->destroy)
+				curr_tank = next_tank;
+			else
+				curr_tank = global.get_next_tank(&order_wrapped);
+			death_substitute = true;
+			next_tank        = nullptr;
+			need_update      = true;
+		}
+
+		if (need_update) {
+			update_screen     = true;
+			fire              = false;
+			global.updateMenu = true;
+			global.set_curr_tank(curr_tank);
+			return true;
+		}
+	} // End of checking for tank advancement
+
+	return false;
+}
+
+
+static inline void change_wind_strength ()
+{
+	if (!env.windvariation || !env.windstrength)
+		return;
+	else {
+		global.wind = global.lastwind
+		          + static_cast<double>(rand () % (env.windvariation * 100)) / 100
+		          - static_cast<double>(env.windvariation) / 2.;
+		if (global.wind > (env.windstrength / 2))
+			global.wind = static_cast<double>(env.windstrength) / 2.;
+		else if (global.wind < (-env.windstrength / 2))
+			global.wind = static_cast<double>(env.windstrength) / -2.;
+
+		global.lastwind = global.wind;
+	}
+
+	// make sure game clients have up to date wind data
+#  ifdef NETWORK
+	char buffer[64];
+	sprintf(buffer, "WIND %f", global.wind);
+	env.sendToClients(buffer);
+#  endif // NETWORK
+}
+
+
+// See whether decorations must be reduced or time can be wasted
+// to achieve the set FPS.
+static inline void check_fps(ObjectUpdater* upd)
+{
+	if ( !global.skippingComputerPlay ) {
+		game_us_needed = game_us_get();
+		int32_t us_unused = us_per_frame - game_us_needed;
+
+		if ( us_unused < 500 ) {
+			// Stop adding decoration:
+			if (!global.hasTooMuchDeco)
+				global.hasTooMuchDeco = true;
+
+			// If more us where needed than available, there is already
+			// too much deco on the screen.
+			// All smoke is forwarded in aging now, so they would get
+			// deleted sooner.
+			if (us_unused < 0) {
+				int32_t agemod = (us_unused / -1000) + 1;
+
+				// Don't age more than 5 frames:
+				if (agemod > 5)
+					agemod = 5;
+				upd[smkIdx].setForceAge(agemod);
+			}
+		} else if ( global.hasTooMuchDeco && (us_unused > 1000) ) {
+			// (Re-)enable deco
+			global.hasTooMuchDeco = false;
+
+			// Normal smoke ageing
+			upd[smkIdx].setForceAge(0);
+		}
+
+		// Sleep what is unused
+		if ( us_unused > 0 ) {
+			USLEEP(us_unused)
+		}
+
+		// Add what has been used until now:
+		game_us_needed += game_us_get();
+	}
+}
+
+
+// Check whether the AI time is up and force a draw if it is.
+// This method does not check whether it is needed and must not
+// be called if global.skippingComputerPlay is false!
+static inline void check_overtime(AICore &aicore)
+{
+	// Check every second whether the AI clock
+	// should be changed:
+	if (second_passed) {
+		global.AI_clock  += SIGN(AI_time_change);
+		AI_time_change    = 0;
+		global.updateMenu = true;
+	}
+
+	// Skip every other frame
+	show_frame = !show_frame;
+
+	// Kill all tanks if skipping time is over
+	if ( (global.AI_clock >= MAX_AI_TIME)
+	  && (winner == WINNER_NO_WIN) ) {
+
+		// Stop the ai first:
+		aicore.stop();
+        while (!aicore.hasExited())
+			std::this_thread::yield();
+
+		// in over-time, kill all tanks
+		TANK* tank = nullptr;
+		global.getHeadOfClass(CLASS_TANK, &tank);
+		while (tank) {
+			// reclaim shield. This is fair, because technically
+			// the bots survive the battle. The tank destruction
+			// is only "for the effect".
+			// And if they bought vengeance items, they'll loose
+			// one now. Expensive enough.
+			if (tank->player)
+				tank->player->reclaimShield();
+			tank->addDamage(nullptr, tank->sh + tank->l + 1);
+			tank->applyDamage();
+			tank->resetFlashDamage();
+			tank->getNext(&tank);
+		}
+
+		global.skippingComputerPlay = false;
+		show_frame                  = true;
+		global.stage                = STAGE_SCOREBOARD;
+		winner                      = WINNER_DRAW;
+
+		// All action is false, or they won't explode
+		has_action.store(   false, ATOMIC_WRITE);
+		has_explosion.store(false, ATOMIC_WRITE);
+
+		// Now make them go bye bye
+		explode_tanks();
+	}
 }
 
-#endif
 
+static inline void check_skiptime()
+{
+	// Check whether to reset the AI_clock
+	int32_t cur_health = 0;
+	int32_t bots_alive = 0;
+
+	for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+		if ( (env.players[i])
+		  && (env.players[i]->tank) ) {
+			cur_health += env.players[i]->tank->l
+						+ env.players[i]->tank->sh;
+			++bots_alive;
+		}
+	}
+
+	int32_t health_delta = skip_health - cur_health;
+
+	if (!skip_health || (health_delta < (bots_alive * 5)))
+		// No (real) damage done, raise AI_Clock
+		++AI_time_change;
+	else if (health_delta > (bots_alive * 25))
+		// Lots of damage, halve the clock
+		global.AI_clock /= 2;
+	else if (global.AI_clock && (health_delta > (bots_alive * 10)) )
+		// Moderate damage, decrease the AI_clock
+		--AI_time_change;
+		// No else, it would be a little damage and that means
+		// do not change the AI_clock at all.
+	skip_health = cur_health;
+}
+
+
+static inline void clear_voices()
+{
+	// Assume one voice per 2 ms to be usable again
+	/// @todo : This must be substituted by a real direct
+	///         control over the voices used. The auto-mixing
+	///         of allegro 4 is just too inefficient.
+	/// However, this way it is a lot better than without any control...
+	if (game_us_needed >= 1000)
+		global.used_voices -= game_us_needed / 500;
+	else
+		--global.used_voices;
+	if (global.used_voices < 0)
+		global.used_voices = 0;
+}
+
+
+static inline void check_winner()
+{
+	bool    all_jedi       = true;
+	bool    all_sith       = true;
+	int32_t player_count   = 0;
+	int32_t last_alive     = -1;
+
+	for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+		TANK* tank = env.players[i]->tank;
+		if (  tank && tank->l && !tank->destroy && tank->player ) {
+			eTeamTypes team = tank->player->team;
+			if (TEAM_SITH != team) all_sith = false;
+			if (TEAM_JEDI != team) all_jedi = false;
+
+			last_alive = i;
+			player_count++;
+		}
+	}
+
+	if      (!player_count)     winner = WINNER_DRAW;
+	else if (all_jedi)          winner = WINNER_JEDI;
+	else if (all_sith)          winner = WINNER_SITH;
+	else if (1 == player_count) winner = last_alive;
+	else                        winner = WINNER_NO_WIN;
+
+	// End skipping play if a winner is known:
+	if (WINNER_NO_WIN != winner) {
+		if (global.stage < STAGE_SCOREBOARD)
+			global.stage = STAGE_SCOREBOARD;
+		global.skippingComputerPlay = false;
+		show_frame = true;
+	}
+}
+
+
+/*****************************************************************************
+ * colorDistance
+ *
+ * Treat two color values as 3D vectors of the form <r,g,b>.
+ * Compute the scalar size of the difference between the two vectors.
+ * *****************************************************************************/
+double colorDistance (int32_t col1, int32_t col2)
+{
+	int32_t r1 = getr (col1);
+	int32_t g1 = getg (col1);
+	int32_t b1 = getb (col1);
+	int32_t r2 = getr (col2);
+	int32_t g2 = getg (col2);
+	int32_t b2 = getb (col2);
+
+	// Treat the colour-cube as a space
+	return FABSDISTANCE3(r1, g1, b1, r2, g2, b2);
+}
+
+
+static inline void delete_destroyed(AICore &aicore)
+{
+	vobj_t* next_obj = nullptr;
+	vobj_t* obj      = nullptr;
+
+	// do not create new FLOATTEXT instance while deletion is in progress
+	aicore.forbidText();
+
+	// Now loop classes and delete destroyed objects
+	for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_) {
+		// Skip tank class, the tanks must be deleted in their special
+		// function explode_tanks() as there is more to do.
+		if (CLASS_TANK == class_)
+			continue;
+
+		eClasses e_class = static_cast<eClasses>(class_);
+
+		global.getHeadOfClass(e_class, &obj);
+		global.lockClass(e_class);
+
+		while(obj) {
+			obj->getNext(&next_obj);
+
+			// Update object if it is destroyed
+			if (obj->destroy) {
+				obj->requireUpdate();
+				obj->update();
+
+				// For deleting the object, the class must be unlocked,
+				// or we'll hit a deadlock with global.removeObject().
+				global.unlockClass(e_class);
+				delete obj;
+				global.lockClass(e_class);
+			}
+			obj = next_obj;
+		} // End of looping objects of one class
+
+		// Finished:
+		global.unlockClass(e_class);
+	} // End of looping classes
+
+	// Eventually re-allow AICore to create FLOATTEXT instances again
+	aicore.allowText();
+}
+
+
+void do_naturals()
+{
+	if (global.naturals_activated >= 5)
+		return;
+
+	if (env.lightning) {
+		int32_t chance = (600 / env.lightning) + 100;
+
+		if (!(rand () % chance)) {
+			try {
+				new BEAM (nullptr, 1 + (rand () % (env.screenWidth - 2)),
+				          MENUHEIGHT + (env.isBoxed ? 1 : 0),
+				          ((rand () % 160) + (360 - 80)) % 360,
+				          SML_LIGHTNING + (rand () % env.lightning), BT_NATURAL);
+				global.naturals_activated++;
+				return;
+			} catch (...) { /* can't do anything here */ }
+		}
+	}      // end of lightning
+
+	// only create meteors and dirt balls if we are not in aim mode on simul turn type
+	if ( (env.turntype == TURN_SIMUL) && (global.stage == STAGE_AIM) )
+		return;
+
+	if (env.meteors) {
+		int32_t chance = (600 / env.meteors) + 100;
+
+		if (!(rand () % chance)) {
+			int32_t ca = ((rand () % 160) + (360 - 80)) % 360;
+			double  mxv = env.slope[ca][0] * 5;
+			double  myv = env.slope[ca][1] * 5;
+
+			try {
+				new MISSILE(nullptr, 1 + (rand () % (env.screenWidth - 2)),
+							MENUHEIGHT + (env.isBoxed ? 1 : 0), mxv, myv,
+							SML_METEOR + (rand () % env.meteors), MT_NATURAL, 1);
+				global.naturals_activated++;
+				return;
+			} catch (...) { /* can't do anything here */ }
+		}
+	}
+
+	if (env.falling_dirt_balls) {
+		int32_t chance = (600 / env.falling_dirt_balls) + 100;
+
+		if (! (rand() % chance) ) {
+			int ca = ((rand() % 100) + (360 - 80) ) % 360;
+			double mxv = env.slope[ca][0] * 5;
+			double myv = env.slope[ca][1] * 5;
+
+			try {
+				new MISSILE(nullptr, 1 + (rand () % (env.screenWidth - 2)),
+				            MENUHEIGHT + (env.isBoxed ? 1 : 0), mxv, myv,
+				            DIRT_BALL + ( rand() % env.falling_dirt_balls),
+				            MT_NATURAL, 1);
+				global.naturals_activated++;
+			} catch (...) { /* can't do anything here */ }
+		}
+	}
+}
+
+
+static inline void draw_FPS_Counter()
+{
+	++FPS_counter;
+
+	if (second_passed) {
+		FPS_last    = FPS_counter;
+		FPS_counter = 0;
+	}
+
+	int32_t fc = BLUE;
+	int32_t bc = GREY;
+	if (FPS_last < (env.frames_per_second - 5)) {
+		fc = DARK_RED;
+		bc = DARK_GREEN;
+	} else if (FPS_last > (env.frames_per_second + 5)) {
+		fc = TURQUOISE;
+		bc = SILVER;
+	}
+
+	textprintf_ex (global.canvas, font, FPS_pos + 1, MENUHEIGHT + 11,
+				   bc, -1, "% 4d FPS", FPS_last);
+	textprintf_ex (global.canvas, font, FPS_pos,     MENUHEIGHT + 10,
+				   fc, -1, "% 4d FPS", FPS_last);
+
+	global.make_update(FPS_pos -  1, MENUHEIGHT +  5,
+					   FPS_pos + 60, MENUHEIGHT + 20);
+
+}
+
+
+static inline void draw_objects(AICore &aicore)
+{
+	vobj_t* obj = nullptr;
+
+	has_deco.store(false, ATOMIC_WRITE);
+	set_clip_rect (global.canvas, 0, MENUHEIGHT,
+				   (env.screenWidth-1), (env.screenHeight-1));
+
+	// do not create new FLOATTEXT instance while drawing is in progress
+	aicore.forbidText();
+
+	for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_) {
+
+		global.getHeadOfClass(static_cast<eClasses>(class_), &obj);
+		while(obj) {
+
+			if (show_frame) {
+				obj->draw();
+				obj->update();
+			}
+
+			if ( (false == has_deco.load(ATOMIC_READ))
+			  && ( (CLASS_DECOR_DIRT  == class_)
+			    || (CLASS_DECOR_SMOKE == class_) ) )
+				has_deco.store(true, ATOMIC_WRITE);
+
+			obj->getNext(&obj);
+		} // End of looping objects
+
+	} // End of looping classes
+
+	// Eventually re-allow AICore to create FLOATTEXT instances again
+	aicore.allowText();
+}
+
+
+/// @brief the ingame mini score board
+static inline void draw_mini_scoreboard()
+{
+	int32_t line = MENUHEIGHT + 2;
+
+	for (int i = 0; i < env.maxNumTanks; ++i) {
+		PLAYER *player = env.playerOrder[i];
+
+		assert(player && "ERROR: player in playerOrder is nullptr!");
+
+		if (player) {
+			int32_t color = player->color;
+			const char*   money = Add_Comma(player->money);
+			const char*   name  = player->getName();
+			const char*   team  = player->getTeamName();
+			int32_t       mid_y = line + (env.fontHeight / 2) + 1;
+
+			// Strike through dead players (BLACK background *before* the name)
+			if (!player->tank || player->tank->destroy)
+				hline(global.canvas, 17, mid_y + 1, 276, BLACK);
+
+			// Display team
+			textprintf_ex (global.canvas, font,  16, line + 1, BLACK, -1,
+			               "(%-7s)", team);
+			textprintf_ex (global.canvas, font,  15, line,     color, -1,
+			               "(%-7s)", team);
+
+			// Display player indicator
+			player->drawIndicator(score_name_pos - env.fontHeight - 2,
+			                      line + 1, env.fontHeight - 3);
+
+			// Display name
+			textprintf_ex (global.canvas, font, score_name_pos + 1, line + 1,
+			               BLACK, -1, "%s", name);
+			textprintf_ex (global.canvas, font, score_name_pos, line,
+			               color, -1, "%s", name);
+
+			// Display money
+			textprintf_ex (global.canvas, font, score_money_pos + 1, line + 1,
+			               BLACK, -1, "$%s", money);
+			textprintf_ex (global.canvas, font, score_money_pos, line,
+			               color, -1, "$%s", money);
+
+			// Draw an arrow indicating the current player
+			if (curr_tank == player->tank) {
+				hline(global.canvas, 4, mid_y,     12, color);
+				hline(global.canvas, 5, mid_y + 1, 13, BLACK);
+			}
+
+			// Strike through dead players (color)
+			if (!player->tank || player->tank->destroy)
+				hline(global.canvas, 16, mid_y, 275, color);
+
+			line += env.fontHeight;
+		}
+	}
+
+	global.make_update(0, MENUHEIGHT, 300, line);
+}
+
+
+/// @brief This method draws the top bar with all current information
+void draw_top_bar ()
+{
+	TANK*         tank          = global.get_curr_tank();
+	PLAYER*       player        = tank   ? tank->player              : nullptr;
+	const char*   name          = player ? player->getName ()        : nullptr;
+	const char*   team_name     = player ? player->getTeamName()     : nullptr;
+	int32_t       color         = player ? player->color             : BLACK;
+	int32_t       time_to_fire  = player ? player->time_left_to_fire : 0;
+	int32_t       y1            =  0;
+	int32_t       y2            = 13;
+	int32_t       y3            = 26;
+	static
+	int32_t change_colour = RED;
+
+	// Copy empty top bar background
+	global.updateMenu = false;
+
+	// copy backdrop:
+	set_clip_rect(global.canvas, 0, 0, env.screenWidth - 1, MENUHEIGHT - 1);
+	blit (env.gfxData.topbar, global.canvas, 0, 0, 0, 0, env.screenWidth, MENUHEIGHT);
+
+	// Fill in player info if possible :
+	if (player) {
+		// name is first, as always
+		textout_ex   (global.canvas, font, name, 2, y1 + 1,
+		              GetShadeColor(color, true, PINK), -1);
+		textout_ex   (global.canvas, font, name, 1, y1,
+		              color, -1);
+		textprintf_ex(global.canvas, font, 1, y2, BLACK, -1,
+		              "%s", env.ingame->Get_Line(18));
+
+		// Display set angle. 0 is directly left, 180 points directly right
+		graph_bar_center (50, y2 + 4, color, -(tank->a - 180) / 2, 180 / 2);
+		textprintf_ex(global.canvas, font, 150, y2, BLACK, -1,
+		              "%d", GET_DISP_ANGLE(tank->a));
+
+		// Display set power
+		graph_bar (50, y3 + 4, color, (tank->p) / (MAX_POWER/90), 90);
+		textprintf_ex(global.canvas, font,   1, y3, BLACK, -1,
+		              "%s", env.ingame->Get_Line(19));
+		textprintf_ex(global.canvas, font, 150, y3, BLACK, -1,
+		              "%d", tank->p);
+
+		// Display the team name
+		textprintf_ex(global.canvas, font, 200, y3, BLACK, -1,
+		              "%s: %s", env.ingame->Get_Line(20), team_name);
+
+		// Display weapon if chosen
+		if (tank->cw < WEAPONS) {
+			int32_t       amt = tank->player->nm[tank->cw]
+			                  / weapon[tank->cw].getDelayDiv();
+			int32_t col = BLACK;
+
+			// Forcibly changed weapons (previous out of ammo) flash red/white
+			// on each update.
+			if (tank->player->changed_weapon) {
+				col = change_colour;
+
+				if (RED == change_colour)
+					change_colour = WHITE;
+				else
+					change_colour = RED;
+			}
+			textprintf_ex(global.canvas, font, 180, y1, col, -1,
+			              "%s: %d", weapon[tank->cw].getName(), amt);
+		} else
+			textprintf_ex(global.canvas, font, 180, y1, BLACK, -1,
+			              "%s: %d", item[tank->cw - WEAPONS].getName(),
+			              tank->player->ni[tank->cw - WEAPONS]);
+
+		// Show the weapon / item icon
+		draw_sprite(global.canvas, env.stock[ (tank->cw > 0) ? tank->cw : 1], 700, 1);
+
+		// Eventually print out money and Fuel
+		textprintf_ex(global.canvas, font, 386, y1, BLACK, -1,
+		              "$%s", Add_Comma(tank->player->money));
+		textprintf_ex(global.canvas, font, 386, y2, BLACK, -1,
+		              "%s: %d", env.ingame->Get_Line(21),
+		              tank->player->ni[ITEM_FUEL]);
+	} // End of displaying player info
+
+
+	// Display round information
+	textprintf_ex(global.canvas, font, 500,  y1, BLACK, -1,
+	              "%s %d/%d", env.ingame->Get_Line(12),
+	              env.rounds - global.currentround, env.rounds);
+
+	// If a tank status is set, display it
+	if (global.tank_status[0])
+		textprintf_ex(global.canvas, font, 350, y3, global.tank_status_colour,
+		              -1, "%s", global.tank_status);
+
+	// Show the wind blowing (if configured)
+	if (env.windstrength > 0) {
+		textprintf_ex(global.canvas, font, 500, y2, BLACK, -1,
+		              "%s", env.ingame->Get_Line(22));
+
+		int32_t wcol1 = global.wind > 0 ? 1 : 0;
+		int32_t wcol2 = global.wind < 0 ? 1 : 0;
+
+		rect    (global.canvas, 540,                          y2 +  4,
+		                        542 + (env.windstrength * 4), y2 + 12, BLACK);
+		rectfill(global.canvas, 541 + (env.windstrength * 2), y2 +  5,
+		    541 + (global.wind * 4) + (env.windstrength * 2), y2 + 11,
+		         makecol(200 * wcol1, 200 * wcol2, 0) );
+	}
+
+	// Print AI Skip time or chess style clock if set
+	if ( (global.AI_clock > -1) && (global.AI_clock <= MAX_AI_TIME) )
+		textprintf_ex(global.canvas, font, 500, y3, BLACK, -1,
+		              "AI Time: %d", MAX_AI_TIME - global.AI_clock);
+	else if (env.maxFireTime)
+		textprintf_ex(global.canvas, font, 500, y3, BLACK, -1,
+		              "Time: %d", time_to_fire);
+
+	// Update and be done
+	global.stopwindow = 1;
+	global.make_update (0, 0, env.screenWidth, MENUHEIGHT);
+	global.stopwindow = 0;
+}
+
+
+/// Let all tanks explode that are destroyed
+/// @return true if at least one tank goes bye bye
+static inline bool explode_tanks()
+{
+	// return if something is exploding already
+	if (has_explosion.load(ATOMIC_READ))
+		return true; // true, because an explosion is present.
+
+	TANK* tank       = nullptr;
+	TANK* tmp        = nullptr;
+	bool  res        = false;
+	bool  tanks_left = false;
+
+	// Check how many tanks are still alive and whether they are from
+	// different teams
+	bool all_jedi       = true;
+	bool all_sith       = true;
+	bool all_jedi_alive = true;
+	bool all_sith_alive = true;
+	bool do_explode     = false;
+
+	global.getHeadOfClass(CLASS_TANK, &tank);
+
+	while (tank) {
+		// Look for teams for any tanks including exploding ones
+		if (tank->player && (TEAM_JEDI != tank->player->team) )
+			all_jedi = false;
+		if (tank->player && (TEAM_SITH != tank->player->team) )
+			all_sith = false;
+
+		// Look for alive tanks
+		if ( (tank->l > 0) && !tank->destroy) {
+			tanks_left = true;
+
+			// Note down if alive tanks are from other teams
+			if (tank->player && (TEAM_JEDI != tank->player->team) )
+				all_jedi_alive = false;
+			if (tank->player && (TEAM_SITH != tank->player->team) )
+				all_sith_alive = false;
+
+		} else
+			do_explode = true;
+
+		tank->getNext(&tank);
+	}
+
+	// Return if no tank is about to explode:
+	if (!do_explode)
+		return false;
+
+	// If tanks are left that are only jedi or sith, vengeance is disallowed:
+	bool allow_vengeance = (tanks_left && !all_jedi && !all_sith);
+
+	// Now explode what has to go
+	global.getHeadOfClass(CLASS_TANK, &tank);
+	while (tank) {
+
+		tank->getNext(&tmp);
+
+		// If the tank is now destroyed, let it explode
+		if (tank->destroy) {
+
+			/* Second level of vengeance allowance:
+			 * If, for example, three tanks are left, one neutral, and two of
+			 * the same team. And the neutral and one team tank are about to
+			 * explode. Then the team tank must not trigger vengeance, because
+			 * their team already won. However, the neutral tank might surely
+			 * detonate in a firestorm and maybe force a draw.
+			 */
+			bool do_vengeance = allow_vengeance;
+			if ( do_vengeance
+			  && ( (all_jedi_alive && (TEAM_JEDI == tank->player->team))
+			    || (all_sith_alive && (TEAM_SITH == tank->player->team)) ) )
+				do_vengeance = false;
+
+			tank->explode(do_vengeance);
+
+			has_action.store(   true, ATOMIC_WRITE); // For sure.
+			has_explosion.store(true, ATOMIC_WRITE); // It'll go now.
+			res = true;
+
+			// count human player reduction
+			if ( (tank->player)
+			  && ( (HUMAN_PLAYER   == tank->player->type)
+				|| (NETWORK_CLIENT == tank->player->type) ) )
+				--human_players;
+
+			// Now the tank has to be removed, but take care
+			// of the current and next tank if they are this
+			if (curr_tank == tank)
+				curr_tank = nullptr;
+			if (next_tank == tank)
+				next_tank = nullptr;
+
+			// Remove from order array
+			global.removeTank(tank);
+
+			delete tank;
+			tank = nullptr;
+		} // End of handling tank destruction
+
+		tank = tmp;
+		tmp  = nullptr;
+	}
+
+	return res;
+}
+
+
+/// @brief Wrapper to automatically trigger firing in simultaneous mode, too.
+static inline void fire_weapon()
+{
+	assert( (STAGE_AIM == global.stage)
+	  && " ERROR: fire_weapon() called, but not STAGE_AIM!");
+	global.stage = STAGE_FIRE;
+
+	if (curr_tank && !curr_tank->destroy) {
+		has_action.store(true);
+		curr_tank->reactivate_shield();
+		curr_tank->simActivateCurrentSelection();
+	}
+
+	// Have everything launched in simultaneous mode
+	if (TURN_SIMUL == env.turntype) {
+		TANK*   tank      = nullptr;
+
+		global.getHeadOfClass(CLASS_TANK, &tank);
+		while (tank) {
+			if (tank->player->skip_me)
+				tank->player->skip_me = false;
+			else {
+				has_action.store(true);
+				tank->activateCurrentSelection();
+			}
+			tank->player->time_left_to_fire = env.maxFireTime;
+			tank->getNext(&tank);
+		}
+
+		assert( (STAGE_FIRE == global.stage)
+			 && "ERROR: global.stage changed illegally!");
+	}
+
+	fire = false;
+}
+
+
+// Draws indication bar
+static inline void graph_bar(int32_t x, int32_t y, int32_t col, int32_t actual,
+                      int32_t max_)
+{
+	rect     (global.canvas, x,     y,     x + max_ + 2,   y + 8, BLACK);
+	rectfill (global.canvas, x + 1, y + 1, x + 1 + actual, y + 7, col);
+}
+
+
+// Draws indication bar - centered
+static inline void graph_bar_center(int32_t x, int32_t y, int32_t col,
+                             int32_t actual, int32_t max_)
+{
+	rect     (global.canvas, x, y,
+	          x + max_ + 2,              y + 8, BLACK);
+	rectfill (global.canvas, x + 1 + max_ / 2, y + 1,
+	          x + 1 + actual + max_ / 2, y + 7, col);
+}
+
+
+// do new round preparations
+static inline void init_new_round()
+{
+	// First env,
+    env.newRound();
+
+    // then the players in case the campaign mode rise kicks in
+    for (int32_t i = 0; i < env.numGamePlayers; ++i)
+        env.players[i]->newRound();
+
+	// finally global, so campaign mode round is changed after the players.
+	global.newRound();
+
+    // clear floating text
+    FLOATTEXT* txt = nullptr;
+	global.getHeadOfClass(CLASS_FLOATTEXT, &txt);
+	while (txt) {
+		txt->newRound();
+		txt->getNext(&txt);
+	}
+
+	// Initialize the static inline global values, so no old data from a previous
+	// run is carried over. (Unless this is wanted, of course)
+	AI_time_change  = 0;
+	curr_tank       = nullptr;
+	fire            = false;
+	human_players   = 0;
+	us_per_frame    = 1000000 / env.frames_per_second;
+	next_tank       = nullptr;
+	order_wrapped   = false;
+	second_passed   = false;
+	show_frame      = true;
+	skip_health     = 0;
+	update_screen   = true;
+	winner          = WINNER_NO_WIN;
+	game_us_reset();
+
+	// everyone gets to buy stuff. While the (human)
+	// player(s) are at it, generate the next level
+	// in the background.
+	LevelCreator lvlCreator;
+	std::thread gen_land_thread(std::ref(lvlCreator));
+	shop(&lvlCreator);
+	gen_land_thread.join();
+
+	// End each players shopping and count the number of human players
+	for (int32_t i = 0; i < env.numGamePlayers; ++i) {
+		env.players[i]->exitShop();
+		if ( (env.players[i]->type == HUMAN_PLAYER)
+		  || (env.players[i]->type == NETWORK_CLIENT) )
+			human_players++;
+	}
+
+    // set wind
+	if (env.windstrength)
+		global.wind = (rand() % env.windstrength) - (env.windstrength / 2);
+	else
+		global.wind = 0;
+	global.lastwind = global.wind;
+
+	// finalize preparation
+	fi = 1;
+	global.stage = STAGE_AIM;
+	global.updateMenu = true;
+	env.window = BOX(0, 0, env.screenWidth - 1, env.screenHeight - 1);
+}
+
+
+/// @brief Wrapper to combine both human input and AI actions.
+static inline bool manage_input(AICore &aicore)
+{
+	bool done = false;
+
+	if ( curr_tank && curr_tank->player ) {
+		global.updateMenu = false;
+		PLAYER* player    = curr_tank->player;
+		bool    can_fire  = !( has_action.load(ATOMIC_READ)
+		                    || has_explosion.load(ATOMIC_READ) );
+		int32_t result    = player->controlTank(&aicore, can_fire);
+
+		if (CONTROL_QUIT == result)
+			done = true;
+		else if (CONTROL_FIRE == result) {
+			has_action.store(true);
+			next_tank = global.get_next_tank(&order_wrapped);
+
+			if (order_wrapped || (env.turntype != TURN_SIMUL) )
+				fire = true;
+		} else if ( (CONTROL_SKIP == result)
+		         && !human_players
+		         && env.skipComputerPlay
+		         && !global.skippingComputerPlay
+		         && (STAGE_SCOREBOARD > global.stage)
+		         && (WINNER_NO_WIN == winner) ) {
+			global.skippingComputerPlay = true;
+			global.AI_clock = 0;
+		}
+
+		update_screen = false;
+		if (result)
+			global.updateMenu = true;
+	}
+
+	return done;
+}
+
+
+/** @brief Set up the next level to play
+  *
+  * This must work in parallel with the shop(), so any drawing must
+  * lock the land, do the drawing and unlock it again.
+**/
+static inline void set_level_settings(LevelCreator* lcr)
+{
+	//  -------------------------
+	// ===  Choosing colours   ===
+	//=============================
+	lcr->working_on(1);
+
+	// Choose first gradients for sky and land
+
+	// First the land:
+	if (lcr->can_work()) {
+		global.curland = (rand () % LANDS)
+							+ (CT_CRISPY == env.colourTheme ? LANDS : 0);
+		if (!env.gfxData.land_gradient_strips[global.curland]) {
+
+			global.lockLand();
+
+			env.gfxData.land_gradient_strips[global.curland]
+				= create_gradient_strip (land_gradients[global.curland],
+				                         (env.screenHeight - MENUHEIGHT));
+
+			global.unlockLand();
+		}
+	}
+
+	// Then the sky
+	if (lcr->can_work()) {
+		global.cursky = (rand () % SKIES)
+		              + (CT_CRISPY == env.colourTheme ? SKIES : 0);
+
+		if (!env.gfxData.sky_gradient_strips[global.cursky]) {
+			global.lockLand();
+
+			env.gfxData.sky_gradient_strips[global.cursky]
+				= create_gradient_strip (sky_gradients[global.cursky],
+				                         (env.screenHeight - MENUHEIGHT));
+
+			global.unlockLand();
+		}
+	}
+	BITMAP* sky_gradient_strip = env.gfxData.sky_gradient_strips[global.cursky];
+
+	//  -------------------------
+	// === Rendering Landscape ===
+	//=============================
+	lcr->working_on(2);
+	if (lcr->can_work())
+		generate_land (lcr, rand (), 0, env.screenHeight);
+
+
+	//  -------------------------
+	// ===    Check Colours    ===
+	//=============================
+	if (lcr->can_work()) {
+		int32_t peak_height = env.screenHeight;
+		for (int32_t z = 0; lcr->can_work() && (z < env.screenWidth); ++z) {
+			if (peak_height > global.surface[z].load())
+				peak_height = global.surface[z].load(ATOMIC_READ);
+		}
+
+		int32_t min_dist    = 128; // start with this colour distance wanted
+		int32_t max_tries   = 16;  // These many tries before lowering the distance
+		int32_t cur_try     = 1;
+		int32_t bottom      = env.screenHeight - MENUHEIGHT;
+		int32_t max_y       = std::min(env.screenHeight - peak_height,
+		                               env.screenHeight - MENUHEIGHT);
+		bool    has_colours = false;
+
+		while (!has_colours && lcr->can_work()) {
+			has_colours = true;
+
+			for (int32_t y = 2; lcr->can_work() && has_colours && (y < max_y); ++y) {
+				if (colorDistance(
+						getpixel(sky_gradient_strip,  0, bottom - y),
+						getpixel(global.terrain, 0, env.screenHeight - y) )
+						< min_dist)
+					has_colours = false;
+			}
+
+			if (!has_colours && lcr->can_work()) {
+				// Create new strip:
+				global.cursky = (rand () % SKIES)
+								   + (CT_CRISPY == env.colourTheme ? SKIES : 0);
+
+				if (!env.gfxData.sky_gradient_strips[global.cursky]) {
+					global.lockLand();
+
+					env.gfxData.sky_gradient_strips[global.cursky]
+						= create_gradient_strip (sky_gradients[global.cursky],
+												(env.screenHeight - MENUHEIGHT));
+
+					global.unlockLand();
+				}
+				sky_gradient_strip = env.gfxData.sky_gradient_strips[global.cursky];
+
+				// Advance try and check:
+				if (++cur_try > max_tries) {
+					cur_try   = 1;
+					min_dist /= 2;
+
+					// Break if min_dist is reduced to 1:
+					if (min_dist < 2)
+						has_colours = true;
+				} // end of advancing tries
+			} // end of handling wrong colours
+		} // End of searching suitable sky colours
+	} // end of thread not to be killed
+
+	//  -------------------------
+	// ===    Rendering Sky    ===
+	//=============================
+	lcr->working_on(3);
+
+	if (lcr->can_work()) {
+		if (env.sky
+		  && ( (env.sky->w  != env.screenWidth)
+		    || (env.sky->h != (env.screenHeight - MENUHEIGHT) ) ) ) {
+			destroy_bitmap(env.sky);
+			env.sky = nullptr;
+		}
+
+		// see if we want a custom background
+		if (env.custom_background && env.bitmap_filenames) {
+			global.lockLand();
+			if (env.sky)
+				destroy_bitmap(env.sky);
+			env.sky = load_bitmap(env.bitmap_filenames[
+			                      rand() % env.number_of_bitmaps ], nullptr);
+			global.unlockLand();
+		}
+
+		// if we do not have a custom background (or do not want one) create a new background
+		if (!env.custom_background || !env.sky) {
+			global.lockLand();
+
+			if (!env.sky)
+				env.sky = create_bitmap(env.screenWidth,
+				                           env.screenHeight - MENUHEIGHT);
+
+			global.unlockLand();
+
+			generate_sky (lcr, sky_gradients[global.cursky],
+							(env.ditherGradients ? GENSKY_DITHERGRAD : 0 ) |
+							(env.detailedSky ? GENSKY_DETAILED : 0 )  );
+		}
+	}
+
+	lcr->working_on(4);
+}
+
+
+/// @brief tank placement and player ordering
+static inline void set_tank_settings()
+{
+	//  -------------------------
+	// ===   Tank Placement    ===
+	//=============================
+
+	// Distribute tanks over the landscape
+	abool_t taken[MAXPLAYERS] = { ATOMIC_VAR_INIT(false) };
+	int32_t middle            = global.numTanks / 2;
+
+	global.getHeadOfClass(CLASS_TANK, &curr_tank);
+	while (curr_tank) {
+		int32_t x = rand () % global.numTanks;
+		while (taken[x]){
+			bool go_up = x < middle ? true : false;
+			while (taken[x] && (x > 0) && (x < (global.numTanks - 1)) )
+				x += go_up ? 1 : -1;
+			if (taken[x])
+				x = rand () % global.numTanks;
+		}
+
+		/* Note: this is a lot faster than the previous approach, because
+		 * the chance to fail if only one place is left is very high, while
+		 * the chance to go into the direction of the last free spot is 50%.
+		 */
+
+		if (!taken[x]) {
+			taken[x] = true;
+
+			int32_t tx = (x + 1) * (env.screenWidth / (global.numTanks + 1));
+			int32_t ty = env.screenHeight - global.surface[tx].load();
+
+			curr_tank->newRound (tx, ty);
+			curr_tank->getNext(&curr_tank);
+		}
+	}
+
+	//  --------------------------
+	// === Determine Tank Order ===
+	//==============================
+
+	for (int32_t z = 0; z < MAXPLAYERS; z++) {
+		global.order[z]    = nullptr;
+		env.playerOrder[z] = nullptr;
+	}
+	env.maxNumTanks = global.numTanks;
+
+	// Distribute tanks in the order array
+	int32_t place = 0;
+	global.getHeadOfClass(CLASS_TANK, &curr_tank);
+	while (curr_tank) {
+		global.order[place++] = curr_tank;
+		curr_tank->getNext(&curr_tank);
+	}
+
+	// Mix up the order if it is wanted to be randomized
+	if ( (env.turntype == TURN_RANDOM)
+	  || (env.turntype == TURN_SIMUL)) {
+		for (int32_t index = 0; index < env.maxNumTanks; ++index) {
+			for (int32_t round = 0; round < middle; ++round) {
+				int32_t target = rand () % global.numTanks;
+				if (target != index) {
+					TANK* tmp_tank       = global.order[index];
+					global.order[index]  = global.order[target];
+					global.order[target] = tmp_tank;
+				}
+			}
+		}
+	}
+
+	// Otherwise sort the order array
+	else {
+		bool sorted = false;
+		while (!sorted) {
+			sorted = true;
+			for (int32_t index = 0; index < env.maxNumTanks - 1; ++index) {
+				bool swap = false;
+				if (env.turntype == TURN_HIGH) {
+					if (global.order[index    ]->player->score
+					  < global.order[index + 1]->player->score)
+					swap = true;
+				} else if (env.turntype == TURN_LOW) {
+					if (global.order[index    ]->player->score
+					  > global.order[index + 1]->player->score)
+					swap = true;
+				}
+				if (swap) {
+					TANK *tempTank = global.order[index];
+					global.order[index] = global.order[index + 1];
+					global.order[index + 1] = tempTank;
+					sorted = false;
+				}
+			}
+		}
+	}
+
+	// Create the ordered list of players
+	// Since tanks get deleted as they are destroyed, we loose the information
+	// about their order, but the player order list will be complete through the
+	// entire round.
+	int32_t max_name_len = 0;
+	int32_t max_team_len = 0;
+
+	for (int i = 0; i < env.maxNumTanks; ++i) {
+		env.playerOrder[i] = global.order[i]->player;
+
+		int32_t name_len = text_length(font, env.playerOrder[i]->getName());
+		int32_t team_len = text_length(font, env.playerOrder[i]->getTeamName());
+
+		if (name_len > max_name_len)
+			max_name_len = name_len;
+		if (team_len > max_team_len)
+			max_team_len = team_len;
+
+		// Reset tank flash damage and activate their first shields:
+		if (env.playerOrder[i]->tank) {
+			env.playerOrder[i]->tank->resetFlashDamage();
+			env.playerOrder[i]->tank->reactivate_shield();
+		}
+	}
+
+	// Set name and money position according to the maximum lengths
+	score_name_pos  = 16 + (2 * env.fontHeight) + max_team_len;
+	score_money_pos = score_name_pos + (2 * env.fontHeight) + max_name_len;
+
+	// FPS is shown on the right top corner:
+	FPS_pos = env.screenWidth - text_length(font, "XXXX FPS ");
+}
+
+
+/// @brief the [e]nd [o]f [r]ound score board
+/// If the stage is STAGE_ENDGAME, credits and points are awarded and the
+/// function waits for user input before it returns
+static inline void draw_eor_scoreboard()
+{
+	// Clear key buffer
+	if (STAGE_ENDGAME == global.stage) {
+		while ( keypressed() )
+			readkey();
+	}
+
+	// check to see if we have a winner or we just got out early
+	if ( (winner != WINNER_NO_WIN)
+	  && !global.demo_mode
+	  && !global.isCloseBtnPressed() ) {
+
+		// Re-Check for winner - This might have changed due to
+		// dying wrath devices that went off - should not happen. Really.
+		if (STAGE_ENDGAME == global.stage) {
+			check_winner();
+
+			// Eventually credit winner(s)
+			env.creditWinners(winner);
+		}
+
+		// Now the scores can be displayed
+		int32_t lh = 14; // The line height. If you need to change it, do it here.
+		int32_t pd = 10; // Padding. How much space to the board border.
+
+		// Find out longest player name and score length do determine the
+		// score board size and score entry positions
+		char    head_name[5]   = "Name";
+		char    head_score[30] = { 0 };
+		snprintf(head_score, 29, " %6s %6s %6s %6s", "Kills", "Killed", "Diff", "Won");
+
+		int32_t namLen      = text_length(font, head_name);
+		int32_t scoLen      = text_length(font, head_score);
+
+		for (int32_t z = 0; z < env.numGamePlayers; z++) {
+			int32_t curLen = text_length(font, env.players[z]->getName());
+			if (curLen > namLen)
+				namLen = curLen;
+			char scoTxt[30] = { 0 };
+			snprintf(scoTxt, 29, " %6d %6d %6d %6d",
+						env.players[z]->kills,
+						env.players[z]->killed,
+						env.players[z]->killed - env.players[z]->kills,
+						env.players[z]->score);
+			curLen = text_length(font, scoTxt);
+			if (curLen > scoLen)
+				scoLen = curLen;
+		}
+
+		// Now calculate the dimensions of our score board.
+		int32_t w = namLen + scoLen + (2 * pd);
+		int32_t h = ((env.numGamePlayers + 4) * lh) + (2 * pd);
+		int32_t x = env.halfWidth  - (w / 2);
+		int32_t y = env.halfHeight - (h / 2);
+
+		// Draw the background and the border
+		global.make_update (x, y, w, h);
+
+		rectfill(global.canvas, x, y, x + w, y + h, BLACK);
+		rect    (global.canvas, x, y, x + w, y + h, WHITE);
+		rect    (global.canvas, x + 1, y + 1, x + w - 1, y + h - 1, GREY);
+
+		// Show a hint to press a key if this is the endgame board
+		if (STAGE_ENDGAME == global.stage) {
+			global.make_update (x, y + h, w, env.fontHeight + 6);
+			textout_centre_ex(global.canvas, font, "Press any key to exit",
+			                  x + (w / 2) + 2, y + h + 6, BLACK, -1);
+			textout_centre_ex(global.canvas, font, "Press any key to exit",
+			                  x + (w / 2),     y + h + 4, SILVER, -1);
+		}
+
+		// Add the padding now, or it must be summed in everywhere!
+		x += pd;
+		y += pd;
+		w -= 2 * pd;
+		h -= 2 * pd;
+
+		// First title line, the winner
+		if (winner == WINNER_JEDI)
+			textout_centre_ex (global.canvas, font, "Jedi Win!",
+								env.halfWidth, y, WHITE, -1);
+		else if (winner == WINNER_SITH)
+			textout_centre_ex (global.canvas, font, "Sith Win!",
+								env.halfWidth, y, WHITE, -1);
+		else if (winner == WINNER_DRAW)
+			textout_centre_ex (global.canvas, font, "Draw",
+								env.halfWidth, y, WHITE, -1);
+		else
+			textprintf_centre_ex (global.canvas, font,
+									env.halfWidth, y,
+									env.players[winner]->color, -1, "%s: %s",
+									env.ingame->Get_Line(47),
+									env.players[winner]->getName());
+
+		// Second title line: The score is to follow. (Is this needed?)
+		textout_right_ex (global.canvas, font, env.ingame->Get_Line(50),
+							env.halfWidth, y + (2 * lh), WHITE, -1);
+
+		// to make the following easier, skip the three used lines
+		// (two titles, one blank)
+		y += 3 * lh;
+
+		// Third title line, the score board header
+		int32_t scoStart = x + namLen;
+		int32_t scoWidth = scoLen / 4;
+
+		textout_ex    (global.canvas, font, "Name", x, y, WHITE, -1);
+		textprintf_right_ex (global.canvas, font, scoStart + (1 * scoWidth),
+							y, GREEN, -1, " %6s", "Kills");
+		textprintf_right_ex (global.canvas, font, scoStart + (2 * scoWidth),
+							y,   RED, -1, " %6s", "Killed");
+		textprintf_right_ex (global.canvas, font, scoStart + (3 * scoWidth),
+							y, WHITE, -1, " %6s", "Diff");
+		textprintf_right_ex (global.canvas, font, scoStart + (4 * scoWidth),
+							y, WHITE, -1, " %6s", "Won");
+
+
+		// Create the score order.
+		// The scores are ordered by score->diff->kills->killed->name
+		sScore* score_array = sort_scores();
+
+		// And get the head entry:
+		sScore* score = score_array;
+		while (score->prev)
+			score = score->prev;
+
+
+		// Eventually the player scores can be displayed:
+		// (again skip the previous line for easier reading/doing below)
+		y   += lh;
+		int32_t z = 0;
+		while (score) {
+			textout_ex    (global.canvas, font, score->name,
+							x, y + (z * lh), score->color, -1);
+			textprintf_right_ex (global.canvas, font, scoStart + (1 * scoWidth),
+								y + (z * lh), GREEN, -1, " %6d", score->kills);
+			textprintf_right_ex (global.canvas, font, scoStart + (2 * scoWidth),
+								y + (z * lh),   RED, -1, " %6d", score->killed);
+			textprintf_right_ex (global.canvas, font, scoStart + (3 * scoWidth),
+								y + (z * lh), score->diff < 0 ? RED : GREEN, -1,
+								" %6d", score->diff);
+			textprintf_right_ex (global.canvas, font, scoStart + (4 * scoWidth),
+								y + (z * lh), WHITE, -1, " %6d", score->score);
+			++z;
+			score = score->next;
+		}
+
+		// If this is the end-board, display here and wait for user input:
+		if (STAGE_ENDGAME == global.stage) {
+			global.do_updates();
+
+			// Wait until a key is pressed
+			while (!keypressed() && !mouse_b)
+				LINUX_REST;
+
+			// Clear key buffer
+			while ( keypressed() )
+				readkey();
+		}
+
+		// Clean up
+		delete [] score_array;
+	} // End of handling winner display
+}
+
+
+/** @brief sort players by scores.
+  *
+  * The return value is the pointer to the allocated array, users
+  * must use its prev() pointer to find the head entry.
+  *
+  * @return a pointer to the scores array. This must be deleted.
+**/
+sScore* sort_scores()
+{
+	sScore* scores     = new sScore[env.numGamePlayers];
+	sScore* score_head = scores;
+	sScore* score_tail = scores;
+	sScore* curr       = nullptr;
+
+	for (int32_t z = 0; z < env.numGamePlayers; z++) {
+		curr          = score_head;
+		scores[z]     = *(env.players[z]);
+		scores[z].idx = z; // The game index is needed.
+
+		// Walk to find a lower score:
+		while (curr
+				&& (curr->score > scores[z].score))
+			curr = curr->next;
+
+		// Walk to find a lower diff:
+		while (curr
+				&& (curr->score == scores[z].score)
+				&& (curr->diff   > scores[z].diff))
+			curr = curr->next;
+
+		// Walk to find a lower kills value:
+		while (curr
+				&& (curr->score == scores[z].score)
+				&& (curr->diff  == scores[z].diff)
+				&& (curr->kills  > scores[z].kills))
+			curr = curr->next;
+
+		// Walk to find a higher killed value:
+		while (curr
+				&& (curr->score == scores[z].score)
+				&& (curr->diff  == scores[z].diff)
+				&& (curr->kills == scores[z].kills)
+				&& (curr->killed < scores[z].killed))
+			curr = curr->next;
+
+		// Walk to find a higher name value:
+		while (curr
+				&& (curr->score  == scores[z].score)
+				&& (curr->diff   == scores[z].diff)
+				&& (curr->kills  == scores[z].kills)
+				&& (curr->killed == scores[z].killed)
+				&& (strcmp(curr->name, scores[z].name) < 0))
+			curr = curr->next;
+
+		// If there is a curr, sort the new score before it.
+		if (curr && (curr != &scores[z])) {
+			scores[z].prev = curr->prev;
+			scores[z].next = curr;
+			if (scores[z].prev)
+				scores[z].prev->next = &scores[z];
+			curr->prev = &scores[z];
+			if (score_head == curr)
+				score_head = &scores[z];
+		}
+
+		// Otherwise this is the new tail:
+		else if (score_tail != &scores[z]) {
+			scores[z].prev   = score_tail;
+			score_tail->next = &scores[z];
+			score_tail       = &scores[z];
+		}
+	} // End of sorting scores
+
+	return scores;
+}
+
+
+static inline void update_display()
+{
+	if (show_frame) {
+		// do not show custom mouse cursor while drawing
+		SHOW_MOUSE(nullptr)
+
+		set_clip_rect(global.canvas, 0, 0,
+		              env.screenWidth - 1, env.screenHeight - 1);
+
+		if (update_screen) {
+			update_screen = false;
+			global.make_fullUpdate();
+			global.updateMenu = true;
+		}
+		global.replace_canvas();
+	}
+}
+
+
+
+
+static inline void update_objects(ObjectUpdater* upd)
+{
+	// Start all updater threads
+	for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_)
+		upd[class_].start();
+
+	// Wakeup all at once:
+	updMutex.lock();
+	updCondition.notify_all();
+	updMutex.unlock();
+
+	// Wait for the threads to finish
+	bool has_thread = true;
+	while (has_thread) {
+		has_thread = false;
+		for (int32_t class_ = 0; class_ < CLASS_COUNT; ++class_) {
+			if (!upd[class_].hasDone())
+				has_thread = true;
+		}
+		if (has_thread)
+			std::this_thread::yield();
+	}
+
+	// Reset SDI shot status on all tanks
+	TANK* lt = nullptr;
+	global.getHeadOfClass(CLASS_TANK, &lt);
+	while (lt) {
+		if (lt->player)
+			lt->player->sdi_has_fired.store(false, ATOMIC_WRITE);
+		lt->getNext(&lt);
+	}
+}
+
+
+/// Level Creator Methods implementation
+LevelCreator::LevelCreator()
+{
+	for (int32_t i = 0; i < 4; ++i)
+		in_progress[i] = false;
+}
+
+
+/// The operator is just a wrapper.
+void LevelCreator::operator()()
+{
+	fiVal = 1;
+	set_level_settings(this);
+	fiVal = 0;
+}
+
+void LevelCreator::add_fi()
+{
+	fiLock.lock();
+	++fiVal;
+	fiLock.unlock();
+}
+
+bool LevelCreator::can_work() const
+{
+	return !i_shall_die;
+}
+
+void LevelCreator::die_now()
+{
+	i_shall_die = true;
+}
+
+bool LevelCreator::has_progress()
+{
+	fiLock.lock();
+	bool result = (fiVal > 0);
+	fiVal = 0;
+	fiLock.unlock();
+
+	return result;
+}
+
+bool LevelCreator::is_finished() const
+{
+	return in_progress[3];
+}
+
+void LevelCreator::print_state() const
+{
+	if (in_progress[0]) {
+		draw_sprite (global.canvas, env.misc[1], env.halfWidth - 120, env.halfHeight + 115);
+		textout_centre_ex(global.canvas, font, env.ingame->Get_Line(42),
+							env.halfWidth, env.halfHeight + 116, WHITE, -1);
+		global.make_update(env.halfWidth - 120, env.halfHeight + 115,
+		                   env.misc[1]->w, env.misc[1]->h);
+	}
+
+	if (in_progress[1]) {
+		draw_sprite(global.canvas, env.misc[1], env.halfWidth - 120, env.halfHeight + 155);
+		textout_centre_ex(global.canvas, font, env.ingame->Get_Line(44),
+							env.halfWidth, env.halfHeight + 156, WHITE, -1);
+		global.make_update(env.halfWidth - 120, env.halfHeight + 155,
+			env.misc[1]->w, env.misc[1]->h);
+	}
+
+
+	if (in_progress[2]) {
+		draw_sprite(global.canvas, env.misc[1], env.halfWidth - 120, env.halfHeight + 195);
+		textout_centre_ex(global.canvas, font, env.ingame->Get_Line(43),
+							env.halfWidth, env.halfHeight + 196, WHITE, -1);
+		global.make_update(env.halfWidth - 120, env.halfHeight + 195,
+			env.misc[1]->w, env.misc[1]->h);
+	}
+}
+
+/// @brief Tell the LevelCreator that it does not need to yield any more
+void LevelCreator::work_alone()
+{
+	i_must_yield = false;
+}
+
+
+void LevelCreator::working_on(int32_t what)
+{
+	if ( (what > 0) && (what < 5) ) {
+		add_fi();
+		in_progress[what - 1] = true;
+	}
+}
+
+
+/// @brief yield if it is not working alone
+void LevelCreator::yield()
+{
+	if (i_must_yield)
+		std::this_thread::yield();
+}
diff --git a/src/gameloop.h b/src/gameloop.h
index 934f118..ddcc442 100644
--- a/src/gameloop.h
+++ b/src/gameloop.h
@@ -1,16 +1,67 @@
 #ifndef GAMELOOP_HEADER_FILE__
 #define GAMELOOP_HEADER_FILE__
 
-#include "globaldata.h"
-#include "environment.h"
-
 #define MAX_TEXT_BOUNCE 40
 
-// The massive game loop, re-wrrite here for
+#include "player.h"
+
+/// Helper Class to background level creation
+class LevelCreator
+{
+	volatile bool in_progress[4];
+	volatile bool i_must_yield   = true;
+	volatile bool i_shall_die    = false;
+	CSpinLock     fiLock;
+	int32_t       fiVal          = 0;
+	void add_fi();
+public:
+	explicit LevelCreator();
+	void operator()();
+	bool can_work() const;
+	void die_now();
+	bool has_progress();
+	bool is_finished() const;
+	void print_state() const;
+	void work_alone();
+	void working_on(int32_t what);
+	void yield();
+};
+
+
+/// @brief small struct to order the score board, used by the end-of-game-sorting, too
+struct sScore
+{
+	int32_t     color  = BLACK;
+	int32_t     diff   = 0;
+	int32_t     idx    = -1;
+	int32_t     killed = 0;
+	int32_t     kills  = 0;
+	const char* name   = nullptr;
+	sScore*     next   = nullptr;
+	sScore*     prev   = nullptr;
+	int32_t     score  = 0;
+
+	sScore &operator=(PLAYER &rhs)
+	{
+		color  = rhs.color;
+		idx    = rhs.index;
+		killed = rhs.killed;
+		kills  = rhs.kills;
+		name   = rhs.getName();
+		score  = rhs.score;
+		diff   = kills - killed;
+		return *this;
+	}
+};
+
+
+// The massive game loop, rewritten here for
 // all sorts of reasons.
-#ifdef NEW_GAMELOOP
-int game(GLOBALDATA *global, ENVIRONMENT *env);
-#endif
+void game();
+
+// sort players. The returned scores point to an array that must be deleted.
+sScore* sort_scores();
+
 
 #endif
 
diff --git a/src/gfxData.cpp b/src/gfxData.cpp
new file mode 100644
index 0000000..8fdfbe3
--- /dev/null
+++ b/src/gfxData.cpp
@@ -0,0 +1,295 @@
+#include "main.h"
+#include "gfxData.h"
+
+/** @brief explicit constructor, because Visual C++ needs one.
+**/
+sGfxData::sGfxData()
+{
+	memset(sky_gradient_strips,  0, sizeof(BITMAP*) * ALL_SKIES);
+	memset(land_gradient_strips, 0, sizeof(BITMAP*) * ALL_LANDS);
+	memset(stuff_bar,            0, sizeof(BITMAP*) * 2);
+	memset(explosions,           0, sizeof(BITMAP*) * EXPLOSIONFRAMES);
+	memset(flameFront,           0, sizeof(BITMAP*) * EXPLOSIONFRAMES);
+}
+
+
+/** @brief sGfxData destructor - clean everything up
+  */
+sGfxData::~sGfxData()
+{
+	this->destroy();
+}
+
+
+/// @brief should be called from ENVIRONMENT::destroy();
+void sGfxData::destroy()
+{
+	if (initDone) {
+		if (topbar)
+			destroy_bitmap(topbar);
+		if (topbar_gradient_strip)
+			destroy_bitmap(topbar_gradient_strip);
+		if (stuff_bar[0])
+			destroy_bitmap(stuff_bar[0]);
+		if (stuff_bar[1])
+			destroy_bitmap(stuff_bar[1]);
+		if (stuff_icon_base)
+			destroy_bitmap(stuff_icon_base);
+		if (stuff_bar_gradient_strip)
+			destroy_bitmap(stuff_bar_gradient_strip);
+		if (explosion_gradient_strip)
+			destroy_bitmap(explosion_gradient_strip);
+
+		topbar = nullptr;
+		topbar_gradient_strip = nullptr;
+		stuff_bar[0] = nullptr;
+		stuff_bar[1] = nullptr;
+		stuff_icon_base = nullptr;
+		stuff_bar_gradient_strip = nullptr;
+		explosion_gradient_strip = nullptr;
+
+		if (sky_gradient_strips) {
+			for (int32_t i = 0; i < ALL_SKIES; ++i) {
+				if ( sky_gradient_strips[i] ) {
+					destroy_bitmap(sky_gradient_strips[i]);
+					sky_gradient_strips[i] = nullptr;
+				}
+			}
+		}
+
+		if (land_gradient_strips) {
+			for (int32_t i = 0; i < ALL_LANDS; ++i) {
+				if ( land_gradient_strips[i] ) {
+					destroy_bitmap(land_gradient_strips[i]);
+					land_gradient_strips[i] = nullptr;
+				}
+			}
+		}
+
+		if (explosions) {
+			for (int32_t i = 0; i < EXPLOSIONFRAMES; ++i) {
+				if ( explosions[i] ) {
+					destroy_bitmap(explosions[i]);
+					explosions[i] = nullptr;
+				}
+			}
+		}
+
+		if (flameFront) {
+			for (int32_t i = 0; i < EXPLOSIONFRAMES; ++i) {
+				if ( flameFront[i] ) {
+					destroy_bitmap(flameFront[i]);
+					flameFront[i] = nullptr;
+				}
+			}
+		}
+
+		initDone = false;
+	}
+}
+
+
+/// @brief create the gfx data to hold
+void sGfxData::first_init()
+{
+	// Note: This method is mostly uncommented, because the original
+	// function that did this was uncommented.
+
+	if (initDone)
+		return;
+
+	int32_t colour_theme = static_cast<int32_t>(env.colourTheme);
+	explosion_gradient_strip = create_gradient_strip(explosion_gradients[colour_theme], 200);
+	double expSize     = 25.;
+	double flmSize     = 10.;
+	double expDisperse = 0.;
+	double flmDisperse = 0.;
+
+	for (int32_t i = 0; i < EXPLOSIONFRAMES; ++i) {
+		explosions[i] = create_bitmap (214, 214);
+		flameFront[i] = create_bitmap (600, 30);
+		if (i < EXPLODEFRAMES - 4) {
+			expSize += (107. - expSize) / 3.;
+			flmSize += (300. - flmSize) / 3.;
+		} else if (i < EXPLODEFRAMES) {
+			expSize -= 1.;
+			flmSize -= 1.;
+		} else if (i == EXPLODEFRAMES) {
+			expDisperse = 25.;
+			flmDisperse = 10.;
+		} else {
+			expDisperse += (107. - expDisperse) / 2.;
+			flmDisperse += (300. - flmDisperse) / 2.;
+		}
+
+		clear_to_color(explosions[i], PINK);
+		clear_to_color(flameFront[i], PINK);
+
+		for (int32_t y = std::floor(expSize); y > expDisperse; --y) {
+			double value = pow (static_cast<double>(y) / expSize, i / 4 + 1);
+			int32_t exp_col = getpixel(explosion_gradient_strip, 0,
+									static_cast<int32_t>(value * 200.));
+			circlefill (explosions[i], 107, 107, y, exp_col);
+		}
+		if (ROUND(expDisperse) > 0) {
+			circlefill (explosions[i], 107, 107, ROUND(expDisperse), PINK);
+		}
+
+		for (int32_t y = std::floor(flmSize); y > flmDisperse; --y) {
+			double value = pow (static_cast<double>(y) / flmSize, i / 4 + 1);
+			int32_t flame_col = getpixel (explosion_gradient_strip, 0,
+									static_cast<int32_t>(value * 200.));
+			ellipsefill (flameFront[i], 300, 15, y, y / 20, flame_col);
+		}
+		if (ROUND(flmDisperse) > 0) {
+			ellipsefill (flameFront[i], 300, 15, ROUND(flmDisperse),
+							ROUND(flmSize / 16.), PINK);
+		}
+	}
+
+	topbar                = create_bitmap (env.screenWidth, MENUHEIGHT);
+	topbar_gradient_strip = create_gradient_strip (topbar_gradient, 100);
+
+	if (!env.ditherGradients) {
+		for (int32_t i = 0; i < MENUHEIGHT; ++i) {
+			float adjCount = (100. / MENUHEIGHT) * i;
+			int32_t col = getpixel(topbar_gradient_strip, 0, adjCount);
+			line (topbar, 0, i, env.screenWidth - 1, i, col);
+        }
+	} else {
+		for (int32_t x = 0; x < env.screenWidth; ++x) {
+			for (int32_t y = 0; y < MENUHEIGHT; ++y) {
+				float adjY = (100.0 / MENUHEIGHT) * y;
+				int offset = 0;
+
+				if ((adjY > 1) && (adjY < 99))
+					offset = rand () % 4 - 2;
+
+				int32_t col = getpixel(topbar_gradient_strip, 0, adjY + offset);
+
+				putpixel (topbar, x, y, col);
+			}
+		}
+	}
+
+	stuff_bar[0]    = create_bitmap (STUFF_BAR_WIDTH, STUFF_BAR_HEIGHT);
+	stuff_bar[1]    = create_bitmap (STUFF_BAR_WIDTH, STUFF_BAR_HEIGHT);
+	stuff_icon_base = create_bitmap (STUFF_BAR_WIDTH/10, STUFF_BAR_HEIGHT);
+
+	clear_to_color (stuff_bar[0], PINK);
+	clear_to_color (stuff_bar[1], PINK);
+	clear_to_color (stuff_icon_base, PINK);
+
+	stuff_bar_gradient_strip = create_gradient_strip (stuff_bar_gradient, STUFF_BAR_WIDTH);
+
+	double halfStuffBarHeight = (STUFF_BAR_HEIGHT / 2) - 2;
+
+	for (double x = 0; x < STUFF_BAR_WIDTH; x += 1.) {
+		for (double y = 0; y < STUFF_BAR_HEIGHT; y += 1.) {
+			double sides_dist  = 0.1;
+			double circle_dist = FABSDISTANCE2(x, y, STUFF_BAR_WIDTH - 75, halfStuffBarHeight);
+
+			if (circle_dist < 75.)
+				circle_dist = 1. - (circle_dist / 75.0);
+			else
+				circle_dist = 0.;
+
+			if (x < (STUFF_BAR_HEIGHT/2 - 2))
+				sides_dist -= 0.1 - (x / 150.);
+			else if (x > STUFF_BAR_WIDTH - (STUFF_BAR_HEIGHT/2 - 2))
+				sides_dist -= (x - (STUFF_BAR_WIDTH - halfStuffBarHeight)) / 150.;
+
+			if (y < STUFF_BAR_HEIGHT/2 - 2)
+				sides_dist -= 0.1 - (y / 150.);
+			else
+				sides_dist -= (y - halfStuffBarHeight) / 150.;
+
+			sides_dist -= circle_dist * circle_dist;
+
+			if (sides_dist > (x / 1000.0))
+				sides_dist = x / 1000.0;
+			if (sides_dist < 0)
+				sides_dist = 0;
+			if (circle_dist > 1)
+				circle_dist = 1;
+
+			int32_t offset = (sides_dist + circle_dist) * (STUFF_BAR_WIDTH - 1);
+			if (offset >= STUFF_BAR_WIDTH)
+				offset  = STUFF_BAR_WIDTH - 1;
+
+			int32_t col_a = getpixel(stuff_bar_gradient_strip,
+			                                   0, offset);
+
+			offset = (sides_dist + circle_dist + 0.06) * (STUFF_BAR_WIDTH - 1);
+			if (offset >= STUFF_BAR_WIDTH)
+				offset  = STUFF_BAR_WIDTH - 1;
+
+			int32_t col_b = getpixel(stuff_bar_gradient_strip,
+			                                   0, offset);
+
+			if (x < (STUFF_BAR_WIDTH / 10)) {
+				putpixel (stuff_icon_base, x, y, col_a);
+			}
+
+			if (y < STUFF_BAR_HEIGHT - 5) {
+				putpixel (stuff_bar[0], x, y, col_a);
+				putpixel (stuff_bar[1], x, y, col_b);
+			}
+		}
+	}
+
+	initDone = true;
+}
+
+
+// === Helper Functions ===
+// ========================
+BITMAP *create_gradient_strip (const gradient *grad, int32_t len)
+{
+	BITMAP *strip = create_bitmap (1, len);
+	if (! strip)
+		return nullptr;
+
+	clear_to_color (strip, BLACK);
+
+	for (int32_t currLine = 0; currLine < len; ++currLine) {
+		int32_t color = gradientColorPoint(grad, len, currLine);
+		putpixel (strip, 0, currLine, color);
+	}
+
+	return strip;
+}
+
+int32_t gradientColorPoint(const gradient* grad, double len, double line)
+{
+	int32_t       pointCount = 0;
+	double        point      = line / len;
+	int32_t color      = BLACK;
+
+	for ( ;
+		(point >= grad[pointCount].point) && (grad[pointCount].point != -1);
+		++pointCount) ;
+	pointCount--;
+
+	if (pointCount == -1)
+		color = makecol (grad[0].color.r, grad[0].color.g, grad[0].color.b);
+	else if (grad[pointCount + 1].point == -1)
+		color = makecol(grad[pointCount].color.r,
+						grad[pointCount].color.g,
+						grad[pointCount].color.b);
+	else {
+		double i = (point - grad[pointCount].point)
+		         / (grad[pointCount + 1].point - grad[pointCount].point);
+		int32_t r = ROUND(interpolate (grad[pointCount].color.r,
+									   grad[pointCount + 1].color.r, i));
+		int32_t g = ROUND(interpolate (grad[pointCount].color.g,
+									   grad[pointCount + 1].color.g, i));
+		int32_t b = ROUND(interpolate (grad[pointCount].color.b,
+									   grad[pointCount + 1].color.b, i));
+
+		color = makecol (r, g, b);
+	}
+
+	return color;
+}
+
diff --git a/src/gfxData.h b/src/gfxData.h
new file mode 100644
index 0000000..14d1972
--- /dev/null
+++ b/src/gfxData.h
@@ -0,0 +1,77 @@
+#pragma once
+#ifndef ATANKS_SRC_GFXDATA_H_INCLUDED
+#define ATANKS_SRC_GFXDATA_H_INCLUDED
+
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ * Copyright (C) 2003  Thomas Hudson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * */
+
+#define ALL_SKIES 16
+#define ALL_LANDS 16
+
+#define	STUFF_BAR_WIDTH	 400
+#define	STUFF_BAR_HEIGHT 35
+
+#define EXPLODEFRAMES  18
+#define	DISPERSEFRAMES 10
+#define	EXPLOSIONFRAMES	(EXPLODEFRAMES + DISPERSEFRAMES)
+
+#define EXPLO_CX 107.f
+#define EXPLO_CY 107.f
+#define EXPLO_H  214.f
+#define EXPLO_W  214.f
+
+#define FLAME_CX 300.f
+#define FLAME_CY  15.f
+#define FLAME_H   30.f
+#define FLAME_W  600.f
+
+/// @brief Consolidate global gfx data in a struct to have RAII in effect.
+struct sGfxData
+{
+	explicit sGfxData();
+	~sGfxData();
+
+	void destroy();
+	void first_init();
+
+	BITMAP* sky_gradient_strips[ALL_SKIES];
+	BITMAP* land_gradient_strips[ALL_LANDS];
+	BITMAP* stuff_bar_gradient_strip = nullptr;
+	BITMAP* topbar_gradient_strip    = nullptr;
+	BITMAP* explosion_gradient_strip = nullptr;
+	BITMAP* stuff_bar[2];
+	BITMAP* stuff_icon_base          = nullptr;
+	BITMAP* topbar                   = nullptr;
+	BITMAP* explosions[EXPLOSIONFRAMES];
+	BITMAP* flameFront[EXPLOSIONFRAMES];
+
+private:
+	bool initDone = false;
+};
+
+
+// === Helper Functions ===
+// ========================
+BITMAP* create_gradient_strip (const gradient* grad, int32_t len);
+int32_t gradientColorPoint    (const gradient* grad, double len, double line);
+
+
+
+#endif // ATANKS_SRC_GFXDATA_H_INCLUDED
diff --git a/src/globaldata.cpp b/src/globaldata.cpp
index 1f665fc..930b8c9 100644
--- a/src/globaldata.cpp
+++ b/src/globaldata.cpp
@@ -18,1377 +18,1258 @@
  * */
 
 #include <time.h>
+#include <cassert>
 #include "player.h"
 #include "globaldata.h"
 #include "files.h"
-#include "network.h"
-#include "team.h"
+#include "tank.h"
+#include "sound.h"
+#include "debris_pool.h"
 
-GLOBALDATA::GLOBALDATA ():dataDir(NULL),configDir(NULL),updates(NULL),lastUpdates(NULL),allPlayers(NULL),
-    players(NULL),currTank(NULL),saved_game_list(NULL)
+
+GLOBALDATA::GLOBALDATA()
 {
-  init_curland_lock();
-
-  #ifdef THREADS
-  command_lock = (pthread_rwlock_t*) malloc(sizeof(pthread_rwlock_t));
-  if (command_lock == NULL)
-  {
-    printf("%s:%i: Could not allocate memory for command_lock.\n", __FILE__, __LINE__);
-  }
-  int result = pthread_rwlock_init(command_lock, NULL);
-  switch (result)
-  {
-    case 0:
-       //Successfully initialized
-       break;
-    case EAGAIN:
-       //resource lack
-       printf("%s:%i: Not enough resources to initialize read-write lock.\n", __FILE__, __LINE__);
-       break;
-    case ENOMEM:
-       //out of memory
-       printf("%s:%i: Not enough memory to initialize read-write lock.\n", __FILE__, __LINE__);
-       break;
-    case EPERM:
-       //not authorized
-       printf("%s:%i: Not authorized.\n", __FILE__, __LINE__);
-       break;
-    default:
-       //If the switch ever gets to here, something very wrong happened and pthread_rwlock_init returned
-       //a random value
-       printf("%s:%i: Unknown error code (%i) returned by pthread_rwlock_init.\n", __FILE__, __LINE__, result);
-       break;
-  }
-  #endif
-  tank_status = (char *)calloc(128, sizeof(char));
-  if (!tank_status)
-    {
-      perror ( "globaldata.cpp: Failed allocating memory for tank_status in GLOBALDATA::GLOBALDATA");
-      // exit (1);
-    }
-
-  initialise ();
-  language = LANGUAGE_ENGLISH;
-  sound = 1.0;
-  name_above_tank = TRUE;
-  colourDepth = 0;
-  client_player = NULL;
-  screenWidth = DEFAULT_SCREEN_WIDTH;
-  screenHeight = DEFAULT_SCREEN_HEIGHT;
-  width_override = height_override = 0;
-  temp_screenWidth = screenWidth;
-  temp_screenHeight = screenHeight;
-  halfWidth = screenWidth / 2;
-  halfHeight = screenHeight / 2;
-  menuBeginY = (screenHeight - 400) / 2;
-  if (menuBeginY < 0) menuBeginY = 0;
-  menuEndY = screenHeight - menuBeginY;
-  frames_per_second = FRAMES_PER_SECOND;
-  numPermanentPlayers = 10;
-  violent_death = FALSE;
-/*
-#ifndef DOS
-  cacheCirclesBG = 1;
-#endif
-
-#ifdef DOS
-  cacheCirclesBG = 0;
-#endif
-*/
-  ditherGradients = 1;
-  detailedLandscape = 0;
-  detailedSky = 0;
-  colour_theme = COLOUR_THEME_CRISPY;
-  startmoney = 15000;
-  turntype = TURN_RANDOM;
-  skipComputerPlay = SKIP_HUMANS_DEAD;
-  // dataDir = DATA_DIR;
-  Find_Data_Dir();
-  os_mouse = 1.0;
-  full_screen = FULL_SCREEN_FALSE;
-  interest = 1.25;
-  scoreHitUnit = 75;
-  scoreSelfHit = 0;
-  scoreUnitDestroyBonus = 5000;
-  scoreUnitSelfDestroy = 0;
-  scoreRoundWinBonus = 10000;
-  sellpercent = 0.80;
-  game_name[0] = '\0';
-  load_game = 0.0;
-  campaign_mode = 0.0;
-  saved_game_index = 0;
-  saved_game_list = NULL;
-  max_fire_time = 0.0;
-  close_button_pressed = false;
-  divide_money = 0.0;
-  sound_driver = SOUND_AUTODETECT;
-  update_string = NULL;
-  check_for_updates = 1.0;
-  demo_mode = false;
-  env = NULL;
-  war_quotes = instructions = ingame = NULL;
-  gloat = revenge = retaliation = kamikaze = suicide = NULL;
-  client_message= NULL;
-
-  updates = new BOX[MAXUPDATES];
-  if (!updates)
-    {
-      perror ( "globaldata.cc: Failed allocating memory for updates in GLOBALDATA::GLOBALDATA");
-      // exit (1);
-    }
-  lastUpdates = new BOX[MAXUPDATES];
-  if (!lastUpdates)
-    {
-      perror ( "globaldata.cc: Failed allocating memory for lastUpdates in GLOBALDATA::GLOBALDATA");
-      // exit (1);
-    }
-  updateCount = 0;
-  lastUpdatesCount = 0;
-
-  // players = new PLAYER*[MAXPLAYERS];
-  players = (PLAYER **) calloc( MAXPLAYERS, sizeof(PLAYER *) );
-  if (!players)
-    {
-      perror ( "globaldata.cc: Failed allocating memory for players in GLOBALDATA::GLOBALDATA");
-      // exit (1);
-    }
-  numPlayers = 0;
-  rounds = 5;
-
-  if (allPlayers) free(allPlayers);   // avoid potential leak
-  allPlayers = (PLAYER**) calloc (1, sizeof (PLAYER*));
-  if (!allPlayers)
-    {
-      fprintf (stderr, "Failed allocating memory for players in globaldata.cc\n");
-      // exit (1);
-    }
-
-  for (int count = 0; count < 360; count++)
-    {
-      slope[count][0] = sin (count / (180 / PI));
-      slope[count][1] = cos (count / (180 / PI));
-    }
-  slope[270][1] = 0;
-  configDir = NULL;
-  bIsGameLoaded = true;
-  bIsBoxed = false;
-  iHumanLessRounds = -1;
-  dMaxVelocity = 0.0;	// Will be set in game()
-#ifdef DEBUG_AIM_SHOW
-  bASD = false; // will be set to true after the first drawing of the map
-#endif
-  enable_network = 0.0;
-#ifdef NETWORK
-  listen_port = DEFAULT_LISTEN_PORT;
-#endif
-  strcpy(server_name, "127.0.0.1");
-  strcpy(server_port, "25645");
-  play_music = 1.0;
-  background_music = NULL;
-  music_dir = NULL;
-  unicode = NULL;
-  regular_font = font;
-  draw_background = TRUE;
+	// memset initialization, because Visual C++ 2013 can't do lists, yet.
+	memset(order,       0, sizeof(TANK*)   * MAXPLAYERS);
+	memset(tank_status, 0, sizeof(char)    * 128);
+	memset(heads,       0, sizeof(vobj_t*) * CLASS_COUNT);
+	memset(tails,       0, sizeof(vobj_t*) * CLASS_COUNT);
 }
 
 
-
 GLOBALDATA::~GLOBALDATA()
 {
-  int index;
-
-  if (tank_status)
-  {
-      tank_status[0] = 0;
-      free(tank_status);
-  }
-
-  index = 0;
-  while (sounds[index])
-  {
-     destroy_sample(sounds[index]);
-  }
-  free(sounds);
-
-  if (music_dir)
-     closedir(music_dir);
-  if (unicode)
-     destroy_font(unicode);
-
-  if (war_quotes) delete war_quotes;
-  if (instructions) delete instructions;
-  if (ingame) delete ingame;
-  if (gloat) delete gloat;
-  if (revenge) delete revenge;
-  if (retaliation) delete retaliation;
-  if (kamikaze) delete kamikaze;
-  if (suicide) delete suicide;
-  if (allPlayers) free(allPlayers);
-  if (update_string) free(update_string);
-  #ifdef THREADS
-  if (command_lock)
-  {
-    int result = pthread_rwlock_destroy(command_lock);
-    switch (result)
-    {
-      case 0:
-         //Successfully destroyed
-         break;
-      case EBUSY:
-         //Some thread forgot to unlock the read-write lock
-         printf("%s:%i: Can't destroy because command_lock is still locked.\n", __FILE__, __LINE__);
-         break;
-      case EINVAL:
-         //Invalid lock
-         printf("%s:%i: The lock is invalid.\n", __FILE__, __LINE__);
-         break;
-      default:
-         //Something went wrong
-         printf("%s:%i: Unknown error code (%i) returned by pthread_rwlock_destroy.\n", __FILE__, __LINE__, result);
-         break;
-    }
-    free(command_lock);
-  }
-  destroy_curland_lock();
-  #endif
-}
-//Destroys curland_lock and frees it. If the lock is NULL or an error occurs, a diagnostic message will be printed.
-void GLOBALDATA::destroy_curland_lock() {
-  #ifdef THREADS
-  if (curland_lock != NULL)
-  {
-    int result = pthread_mutex_destroy(curland_lock);
-    switch (result)
-    {
-      case 0:
-        //Successfully destroyed
-        break;
-      case EBUSY:
-        //Some thread forgot to unlock result
-        printf("%s:%i: Lock is still held.\n", __FILE__, __LINE__);
-        break;
-      case EINVAL:
-        //Invalid lock
-        printf("%s:%i: Lock is invalid.\n", __FILE__, __LINE__);
-        break;
-      default:
-        printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_destroy.\n", __FILE__, __LINE__, result);
-        break;
-    }
-    free(curland_lock);
-  }
-  else
-  {
-    printf("%s:%i: Cannot destroy a null mutex.\n", __FILE__, __LINE__);
-  }
-  #endif
+	this->destroy();
 }
-//init_curland_lock() allocates space for curland_lock and then calls pthread_mutex_init to initialize the lock. If the call fails, then this function will print diagnostic messages.
-void GLOBALDATA::init_curland_lock() {
-  #ifdef THREADS
-  curland_lock = (pthread_mutex_t*) malloc(sizeof(pthread_mutex_t));
-  if (!curland_lock)
-  {
-    printf("%s:%i: Could not allocate memory for curland_lock.\n", __FILE__, __LINE__);
-  }
-  int result = pthread_mutex_init(curland_lock, NULL);
-  switch (result)
-  {
-    case 0:
-      //Succesfully initialized
-      break;
-    case EAGAIN:
-      //Not enough resources
-      printf("%s:%i: Not enough resources to create mutex.\n", __FILE__, __LINE__);
-      break;
-    case ENOMEM:
-      printf("%s:%i: Not enough memory to create mutex.\n", __FILE__, __LINE__);
-      break;
-    case EPERM:
-      printf("%s:%i: Not authorized.\n", __FILE__, __LINE__);
-      break;
-    case EBUSY:
-      printf("%s:%i: The mutex is already initialized.\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      printf("%s:%i: Invalid attribute.\n", __FILE__, __LINE__);
-      break;
-    default:
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_init.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
+
+/// @brief goes through the columns from @a left to @a right and sets slide type according to @a do_lock
+void GLOBALDATA::addLandSlide(int32_t left, int32_t right, bool do_lock)
+{
+	// Opt out soon if no landslide is to be done
+	if ( (SLIDE_NONE      == env.landSlideType)
+	  || (SLIDE_TANK_ONLY == env.landSlideType) )
+		return;
+
+	int32_t minX = std::min(left, right);
+	int32_t maxX = std::max(left, right);
+
+	if (minX < 1)
+		minX = 1;
+	if (minX > (env.screenWidth - 1) )
+		minX =  env.screenWidth - 1;
+	if (maxX < 1)
+		maxX = 1;
+	if (maxX > (env.screenWidth - 1) )
+		maxX =  env.screenWidth - 1;
+
+	if (do_lock)
+		memset(&done[minX], 3, sizeof(char) * (maxX - minX + 1) );
+	else
+		memset(&done[minX], 2, sizeof(char) * (maxX - minX + 1) );
 }
-//lock_curland() locks curland and prints diagnostic messages if the lock operation fails.
-//Use get_curland() to read the value instead of directly using lock_curland()/unlock_curland() pairs.
-void GLOBALDATA::lock_curland()
+
+
+void GLOBALDATA::addObject (vobj_t *object)
 {
-  #ifdef THREADS
-  int result = pthread_mutex_lock(curland_lock);
-  switch (result)
-  {
-    case 0:
-      //Got the lock.
-      break;
-    case EINVAL:
-      //Priority too high
-      printf("%s:%i: Either this thread's priority is higher than the mutex's priority, or the mutex is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    case EAGAIN:
-      printf("%s:%i: Too many locks on the mutex.\n", __FILE__, __LINE__);
-      break;
-    case EDEADLK:
-      //We have the lock already
-      printf("%s:%i: Already have lock.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //What error is this?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_lock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
+	if (nullptr == object)
+		return;
+
+	eClasses class_ = object->getClass();
+
+	objLocks[class_].lock();
+
+	/// --- case 1: first of its kind ---
+	if (nullptr == tails[class_]) {
+		heads[class_] = object;
+		tails[class_] = object;
+	}
+
+	/// --- case 2: normal addition ---
+	else {
+		tails[class_]->next = object;
+		object->prev = tails[class_];
+		tails[class_] = object;
+	}
+
+	objLocks[class_].unlock();
 }
-//unlock_curland() will unlock curland and print diagnostic messages if the unlock operation fails.
-void GLOBALDATA::unlock_curland()
+
+
+// Combine both make_update and make_bgupdate with safety checks for
+// the dimensions. This reduces code duplication.
+void GLOBALDATA::addUpdate(int32_t x, int32_t y, int32_t w, int32_t h,
+                           BOX* target, int32_t &target_count)
 {
-  #ifdef THREADS
-  int result = pthread_mutex_unlock(curland_lock);
-  switch (result)
-  {
-    case 0:
-      //Released the lock
-      break;
-    case EPERM:
-      //Forgot to get a lock on the mutex
-      printf("%s:%i: Mutex isn't locked\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      //Uninitialized mutex
-      printf("%s:%i: waiting_sky_lock is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_mutex_unlock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
+	assert (target && "ERROR: addUpdate called with nullptr target!");
+
+	bool combined = false;
+
+	assert ( (w > 0) && (h > 0) ); // No zero/negative updates, please!
+
+	int32_t left   = std::max(x - 1, 0);
+	int32_t top    = std::max(y - 1, 0);
+	int32_t right  = std::min(x + w + 1, env.screenWidth);
+	int32_t bottom = std::min(y + h + 1, env.screenHeight);
+
+	// If the update is outside the screen, it is not needed:
+	if ( (bottom <= 0) /* most common case */
+	  || (left   >= env.screenWidth)
+	  || (right  <= 0)
+	  || (top    >= env.screenHeight) )
+		return;
+
+	assert( (left < right ) );
+	assert( (top  < bottom) );
+
+	if ( combineUpdates && target_count
+	  && (target_count < env.max_screen_updates)) {
+		// Re-purpose BOX::w as x2 and BOX::h as y2:
+		BOX prev(target[target_count - 1].x,
+		         target[target_count - 1].y,
+		         target[target_count - 1].x + target[target_count - 1].w,
+		         target[target_count - 1].y + target[target_count - 1].h);
+		BOX next(left, top, right, bottom);
+
+		if ( (next.w > (prev.x - 3))
+		  && (prev.w > (next.x - 3))
+		  && (next.h > (prev.y - 3))
+		  && (prev.h > (next.y - 3)) ) {
+			next.set(next.x < prev.x ? next.x : prev.x,
+			         next.y < prev.y ? next.y : prev.y,
+			         next.w > prev.w ? next.w : prev.w,
+			         next.h > prev.h ? next.h : prev.h);
+			// recalculate x2/y2 back into w/h
+			target[target_count - 1].set(next.x, next.y,
+			                             next.w - next.x,
+			                             next.h - next.y);
+
+			// Make sure the target update is sane:
+			assert( (target[target_count - 1].w > 0)
+			     && (target[target_count - 1].h > 0) );
+
+			combined = true;
+		}
+	}
+
+	if (!combined)
+		target[target_count++].set(left, top, right - left, bottom - top);
+
+	if (!stopwindow && (target_count <= env.max_screen_updates))
+		env.window_update(left, top, right - left, bottom - top);
 }
-//get_curland() locks curland, reads the value, unlocks curland, then returns the value. The value returned cannot be assigned to.
-//Use this only for reading the value of curland, not for writing.
-//For writing, use lock_curland()/unlock_curland().
-int GLOBALDATA::get_curland()
+
+
+// return true if any living tank is in the given box.
+// left/right and top/bottom are determined automatically.
+bool GLOBALDATA::areTanksInBox(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
 {
-  lock_curland();
-  int c = curland;
-  unlock_curland();
-  return c;
+	TANK* lt = static_cast<TANK*>(heads[CLASS_TANK]);
+
+	while (lt) {
+		// Tank found, is it in the box?
+		if ( (!lt->destroy) && lt->isInBox(x1, y1, x2, y2))
+			return true;
+		lt->getNext(&lt);
+	}
+
+	return false;
 }
 
-//Locks global->command for writing. Unlock with GLOBALDATA::unlock_command().
-void GLOBALDATA::wr_lock_command()
+
+
+// This function checks to see if one full second has passed since the
+// last time the function was called.
+// The function returns true if time has passed. The function
+// returns false if time hasn't passed or it was unable to tell
+// how much time has passed.
+bool GLOBALDATA::check_time_changed()
 {
-  #ifdef THREADS
-  int result = pthread_rwlock_wrlock(command_lock);
-  switch (result)
-  {
-    case 0:
-      //Got the lock.
-      break;
-    case EINVAL:
-      //No lock has been created yet
-      printf("%s:%i: Read-write lock is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    case EDEADLK:
-      //We have the lock already
-      printf("%s:%i: Can't lock for writing because the lock is already locked for either reading or writing.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //What error is this?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_rwlock_wrlock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
+	volatile
+	static time_t last_second    = 0;
+	static time_t current_second = 0;
+
+	time(&current_second);
+
+	if ( current_second == last_second )
+		return false;
+
+	// time has changed
+	last_second = current_second;
+
+	return true;
 }
-//Unlocks a read or a write lock. Do not try to use this function to unlock a lock acquired by calling get_command().
-void GLOBALDATA::unlock_command()
+
+
+/// @brief remove and delete *all* objects stored.
+void GLOBALDATA::clear_objects()
 {
-  #ifdef THREADS
-  int result = pthread_rwlock_unlock(command_lock);
-  switch (result)
-  {
-    case 0:
-      //Released the lock
-      break;
-    case EPERM:
-      //We have released all locks
-      printf("%s:%i: Can't unlock because no read or write lock is currently being held.\n", __FILE__, __LINE__);
-      break;
-    case EINVAL:
-      //Uninitialized read-write lock
-      printf("%s:%i: global->command_lock is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //Something went wrong
-      printf("%s:%i: Unknown error code (%i) returned by pthread_rwlock_unlock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
+	int32_t class_ = 0;
+
+	while (class_ < CLASS_COUNT) {
+		while (tails[class_])
+			delete tails[class_];
+		++class_;
+	}
 }
-//Locks global->command for reading, reads value, then unlocks the variable and returns the value.
-int GLOBALDATA::get_command()
+
+
+// Call before calling allegro_exit()!
+void GLOBALDATA::destroy()
 {
-  #ifdef THREADS
-  int result = pthread_rwlock_rdlock(command_lock);
-  switch (result)
-  {
-    case 0:
-      //Got the lock
-      break;
-    case EINVAL:
-      //No lock has been created.
-      printf("%s:%i: *global->command_lock is uninitialized.\n", __FILE__, __LINE__);
-      break;
-    case EAGAIN:
-      //Can't read since already trying to read
-      //Maybe locks are not being unlocked?
-      printf("%s:%i: Too many read locks already held.\n", __FILE__, __LINE__);
-      break;
-    case EDEADLK:
-      //Obtaining read locks while possessing a write lock is forbidden with read-write locks.
-      printf("%s:%i: Already own write lock; can't get read lock.\n", __FILE__, __LINE__);
-      break;
-    default:
-      //What error is this?
-      printf("%s:%i: Unknown error code (%i) returned by pthread_rwlock_rdlock.\n", __FILE__, __LINE__, result);
-      break;
-  }
-  #endif
-  int c = command;
-  unlock_command();
-  return c;
+	clear_objects();
+
+	if (debris_pool) {
+		delete debris_pool;
+		debris_pool = nullptr;
+	}
+
+	if (canvas)  destroy_bitmap(canvas);     canvas       = nullptr;
+	if (terrain) destroy_bitmap(terrain);    terrain      = nullptr;
+	if (done)         delete [] done;        done         = nullptr;
+	if (fp)           delete [] fp;          fp           = nullptr;
+	if (surface)      delete [] surface;     surface      = nullptr;
+	if (dropTo)       delete [] dropTo;      dropTo       = nullptr;
+	if (velocity)     delete [] velocity;    velocity     = nullptr;
+	if (dropIncr)     delete [] dropIncr;    dropIncr     = nullptr;
+	if (updates)      delete [] updates;     updates      = nullptr;
+	if (lastUpdates)  delete [] lastUpdates; lastUpdates  = nullptr;
 }
 
-/*
-This function saves the global data to a text file. If all goes
-well, TRUE is returned, on error, FALSE is returned.
--- Jesse
-*/
-int GLOBALDATA::saveToFile_Text( FILE *file)
+
+void GLOBALDATA::do_updates ()
 {
-  if (! file) return FALSE;
-
-  setlocale(LC_NUMERIC, "C");
-
-  screenWidth = (int) temp_screenWidth;
-  screenHeight = (int) temp_screenHeight;
-
-  fprintf (file, "*GLOBAL*\n");
-
-  fprintf (file, "NUMPLAYERS=%d\n", numPlayers);
-  fprintf (file, "ROUNDS=%f\n", rounds);
-  fprintf (file, "DITHER=%f\n", ditherGradients);
-  fprintf (file, "DETAILEDSKY=%f\n", detailedSky);
-  fprintf (file, "DETAILEDLAND=%f\n", detailedLandscape);
-  fprintf (file, "STARTMONEY=%f\n", startmoney);
-  fprintf (file, "TURNTYPE=%f\n", turntype);
-  fprintf (file, "INTEREST=%f\n", interest);
-  fprintf (file, "SCOREROUNDWINBONUS=%f\n", scoreRoundWinBonus);
-  fprintf (file, "SCOREHITUNIT=%f\n", scoreHitUnit);
-  fprintf (file, "SCOREUNITDESTROYBONUS=%f\n", scoreUnitDestroyBonus);
-  fprintf (file, "SCOREUNITSELFDESTROY=%f\n", scoreUnitSelfDestroy);
-  fprintf (file, "ACCELERATEDAI=%f\n", skipComputerPlay);
-  fprintf (file, "SELLPERCENT=%f\n", sellpercent);
-  fprintf (file, "ENABLESOUND=%f\n", sound);
-  fprintf (file, "SCREENWIDTH=%d\n", screenWidth);
-  fprintf (file, "SCREENHEIGHT=%d\n", screenHeight);
-  fprintf (file, "OSMOUSE=%f\n", os_mouse);
-  fprintf (file, "NUMPERMANENTPLAYERS=%d\n", numPermanentPlayers);
-  fprintf (file, "LANGUAGE=%f\n", language);
-  fprintf (file, "COLOURTHEME=%f\n", colour_theme);
-  fprintf (file, "FRAMES=%f\n", frames_per_second);
-  fprintf (file, "VIOLENTDEATH=%f\n", violent_death);
-  fprintf (file, "MAXFIRETIME=%f\n", max_fire_time);
-  fprintf (file, "DIVIDEMONEY=%f\n", divide_money);
-  fprintf (file, "CHECKUPDATES=%f\n", check_for_updates);
-  fprintf (file, "NETWORKING=%f\n", enable_network);
-  fprintf (file, "LISTENPORT=%f\n", listen_port);
-  fprintf (file, "SOUNDDRIVER=%f\n", sound_driver);
-  fprintf (file, "PLAYMUSIC=%f\n", play_music);
-  fprintf (file, "FULLSCREEN=%f\n", full_screen);
-  fprintf (file, "***\n");
-  return TRUE;
+	bool isBgUpdNeeded = lastUpdatesCount > 0;
+
+	acquire_bitmap(screen);
+	for (int32_t i = 0; i < updateCount; ++i) {
+		blit( canvas, screen,
+				updates[i].x, updates[i].y, updates[i].x, updates[i].y,
+				updates[i].w, updates[i].h);
+
+		if (isBgUpdNeeded)
+			make_bgupdate( updates[i].x, updates[i].y,
+							updates[i].w, updates[i].h);
+	}
+	release_bitmap(screen);
+	if (!isBgUpdNeeded) {
+		lastUpdatesCount = updateCount;
+		memcpy (lastUpdates, updates, sizeof (BOX) * updateCount);
+	}
+	updateCount = 0;
 }
 
-/*
-This function loads global settings from a text
-file. The function returns TRUE on success and FALSE if
-any erors are encountered.
--- Jesse
-*/
 
-int GLOBALDATA::loadFromFile_Text (FILE *file)
+// Do what has to be done after the game starts
+void GLOBALDATA::first_init()
 {
-  char line[MAX_CONFIG_LINE];
-  int equal_position, line_length;
-  char field[MAX_CONFIG_LINE], value[MAX_CONFIG_LINE];
-  char *result = NULL;
-  bool done = false;
-  double sound_bookmark = 1.0;
-
-  setlocale(LC_NUMERIC, "C");
-  if (! sound)
-    sound_bookmark = sound;
-
- // read until we hit line "*ENV*" or "***" or EOF
-  do
-    {
-      result = fgets(line, MAX_CONFIG_LINE, file);
-      if (! result)     // eof
-        return FALSE;
-      if (! strncmp(line, (char *)"***", 3) )     // end of record
-        return FALSE;
-    }
-  while ( strncmp(line, (char *)"*GLOBAL*", 5) );     // read until we hit new record
-
-  while ( (result) && (!done) )
-    {
-      // read a line
-      memset(line, '\0', MAX_CONFIG_LINE);
-      result = fgets(line, MAX_CONFIG_LINE, file);
-      if (result)
-        {
-          // if we hit end of the record, stop
-          if (! strncmp(line, (char *)"***", 3) )
-            {
-              return TRUE;
-            }
-          // find equal sign
-          line_length = strlen(line);
-          // strip newline character
-          if ( line[line_length - 1] == '\n')
-            {
-              line[line_length - 1] = '\0';
-              line_length--;
-            }
-          equal_position = 1;
-          while ( ( equal_position < line_length) && (line[equal_position] != '=') )
-            equal_position++;
-          // make sure we have valid equal sign
-
-          if ( equal_position <= line_length )
-            {
-              // seperate field from value
-              memset(field, '\0', MAX_CONFIG_LINE);
-              memset(value, '\0', MAX_CONFIG_LINE);
-              strncpy(field, line, equal_position);
-              strcpy(value, & (line[equal_position + 1]));
-              if (! strcasecmp(field, "numplayers") )
-                sscanf(value, "%d", &numPlayers);
-              else if (! strcasecmp(field, "rounds") )
-                sscanf(value, "%lf", &rounds);
-              else if (! strcasecmp(field, "dither"))
-                sscanf(value, "%lf", &ditherGradients);
-              else if (! strcasecmp(field, "detailedsky"))
-                sscanf(value, "%lf", &detailedSky);
-              else if (! strcasecmp(field, "detailedland"))
-                sscanf(value, "%lf", &detailedLandscape);
-              else if (! strcasecmp(field, "startmoney"))
-                sscanf(value, "%lf", &startmoney);
-              else if (! strcasecmp(field, "turntype"))
-                sscanf(value, "%lf", &turntype);
-              else if (! strcasecmp(field, "interest"))
-                sscanf(value, "%lf", &interest);
-              else if (! strcasecmp(field, "scoreroundwinbonus"))
-                sscanf(value, "%lf", &scoreRoundWinBonus);
-              else if (! strcasecmp(field, "scorehitunit"))
-                sscanf(value, "%lf", &scoreHitUnit);
-              else if (! strcasecmp(field, "scoreunitdestroybonus"))
-                sscanf(value, "%lf", &scoreUnitDestroyBonus);
-              else if (! strcasecmp(field, "scoreunitselfdestroy"))
-                sscanf(value, "%lf", &scoreUnitSelfDestroy);
-              else if (! strcasecmp(field, "acceleratedai"))
-                sscanf(value, "%lf", &skipComputerPlay);
-              else if (! strcasecmp(field, "sellpercent"))
-                sscanf(value, "%lf", &sellpercent);
-              else if (! strcasecmp(field, "enablesound"))
-                sscanf(value, "%lf", &sound);
-              else if (! strcasecmp(field, "screenwidth"))
-                sscanf(value, "%d", &screenWidth);
-              else if (! strcasecmp(field, "screenheight"))
-                sscanf(value, "%d", &screenHeight);
-              else if (! strcasecmp(field, "OSMOUSE"))
-                sscanf(value, "%lf", &os_mouse);
-              else if (! strcasecmp(field, "numpermanentplayers"))
-                sscanf(value, "%d", &numPermanentPlayers);
-              else if (! strcasecmp(field, "language") )
-                sscanf(value, "%lf", &language);
-              else if (! strcasecmp(field, "colourtheme") )
-                sscanf(value, "%lf", &colour_theme);
-              else if (! strcasecmp(field, "frames") )
-                sscanf(value, "%lf", &frames_per_second);
-              else if (! strcasecmp(field, "violentdeath") )
-                sscanf(value, "%lf", &violent_death);
-              else if (! strcasecmp(field, "maxfiretime") )
-                sscanf(value, "%lf", &max_fire_time);
-              else if (! strcasecmp(field, "dividemoney") )
-                sscanf(value, "%lf", &divide_money);
-              else if (!strcasecmp(field, "checkupdates")) 
-                sscanf(value, "%lf", &check_for_updates);
-              else if (!strcasecmp(field, "networking"))
-                sscanf(value, "%lf", &enable_network);
-              else if (!strcasecmp(field, "listenport"))
-                sscanf(value, "%lf", &listen_port);
-              else if (!strcasecmp(field, "sounddriver"))
-                sscanf(value, "%lf", &sound_driver);
-              else if (!strcasecmp(field, "playmusic"))
-                sscanf(value, "%lf", &play_music);
-              else if (!strcasecmp(field, "fullscreen"))
-                sscanf(value, "%lf", &full_screen);
-      
-            }    // end of found field=value line
-
-        }     // end of read a line properly
-    }     // end of while not done
-  if (! sound_bookmark)
-    sound = sound_bookmark;
-
-  if (width_override)
-    screenWidth = width_override;
-  if (height_override)
-    screenHeight = height_override;
-
-  halfWidth = screenWidth / 2;
-  halfHeight = screenHeight / 2;
-
-  menuBeginY = (screenHeight - 400) / 2;
-  if (menuBeginY < 0) menuBeginY = 0;
-  menuEndY = screenHeight - menuBeginY;
-
-  if (skipComputerPlay > SKIP_HUMANS_DEAD)
-    skipComputerPlay = SKIP_HUMANS_DEAD;
-
-  return TRUE;
+	// get memory for updates
+	try {
+		updates = new BOX[env.max_screen_updates];
+	} catch (std::bad_alloc &e) {
+		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
+		     << "Failed to allocate memory for updates ["
+		     << e.what() << "]" << endl;
+		exit(1);
+	}
+
+	// get memory for lastUpdates
+	try {
+		lastUpdates = new BOX[env.max_screen_updates];
+	} catch (std::bad_alloc &e) {
+		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
+		     << "Failed to allocate memory for lastUpdates ["
+		     << e.what() << "]" << endl;
+		exit(1);
+	}
+
+	canvas = create_bitmap (env.screenWidth, env.screenHeight);
+	if (!canvas) {
+		cout << "Failed to create canvas bitmap: " << allegro_error << endl;
+		exit(1);
+	}
+
+	terrain = create_bitmap (env.screenWidth, env.screenHeight);
+	if (!terrain) {
+		cout << "Failed to create terrain bitmap: " << allegro_error << endl;
+		exit(1);
+	}
+
+
+	// get memory for the debris pool
+	try {
+		debris_pool = new sDebrisPool(env.max_screen_updates);
+	} catch (std::bad_alloc &e) {
+		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
+		     << "Failed to allocate memory for debris_pool ["
+		     << e.what() << "]" << endl;
+		exit(1);
+	}
+
+
+	try {
+		done     = new int8_t[env.screenWidth]{0};
+		fp       = new int32_t[env.screenWidth]{0};
+		surface  = new ai32_t[env.screenWidth]{ { 0 } };
+		dropTo   = new int32_t[env.screenWidth]{0};
+		velocity = new double[env.screenWidth]{0};
+		dropIncr = new double[env.screenWidth]{0};
+	} catch (std::bad_alloc &e) {
+		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
+		     << "Failed to allocate memory for base data arrays ["
+		     << e.what() << "]" << endl;
+		exit(1);
+	}
+
+	initialise ();
 }
 
 
+/** @brief delegate freeing of a debris item to the debris pool.
+  *
+  * This delegating function, instead of making the debris pool public,
+  * exists as a point where locking, if it becomes necessary, can be
+  * added without having to rewrite a lot of code.
+**/
+void GLOBALDATA::free_debris_item(item_t* item)
+{
+	debris_pool->free_item(item);
+}
 
 
 
-void GLOBALDATA::initialise ()
+int32_t GLOBALDATA::get_avg_bgcolor(int32_t x1, int32_t y1,
+                                    int32_t x2, int32_t y2,
+                                    double xv, double yv)
 {
-  numTanks = 0;
+	// Movement
+	int32_t mvx      = ROUND(10. * xv); // eliminate slow movement
+	int32_t mvy      = ROUND(10. * yv); // eliminate slow movement
+	bool    mv_left  = mvx < 0;
+	bool    mv_right = mvx > 0;
+	bool    mv_up    = mvy < 0;
+	bool    mv_down  = mvy > 0;
+
+	// Boundaries
+	int32_t min_x = 1;
+	int32_t max_x = env.screenWidth - 2;
+	int32_t min_y = env.isBoxed ? MENUHEIGHT + 1 : MENUHEIGHT;
+	int32_t max_y = env.screenHeight - 2;
+
+	// Coordinates
+	int32_t left   = std::max(std::min(x1, x2), min_x);
+	int32_t right  = std::min(std::max(x1, x2), max_x);
+	int32_t centre = (x1 + x2) / 2;
+	int32_t top    = std::max(std::min(y1, y2), min_y);
+	int32_t bottom = std::min(std::max(y1, y2), max_y);
+	int32_t middle = (y1 + y2) / 2;
+
+
+	// Colors:
+	int32_t col_tl, col_tc, col_tr; // top row
+	int32_t col_ml, col_mc, col_mr; // middle row
+	int32_t col_bl, col_bc, col_br; // bottom row
+	int32_t r = 0, g = 0, b = 0;
+
+
+	// Get Sky or Terrain colour, whatever fits:
+	/*---------------------
+	  --- Left side ---
+	  ---------------------*/
+	if ( PINK == (col_tl = getpixel(terrain, left, top)) )
+		col_tl = getpixel(env.sky, left, top);
+	if ( PINK == (col_ml = getpixel(terrain, left, middle)) )
+		col_ml = getpixel(env.sky, left, middle);
+	if ( PINK == (col_bl = getpixel(terrain, left, bottom)) )
+		col_bl = getpixel(env.sky, left, bottom);
+
+	/*---------------------
+	  --- The Center ---
+	---------------------*/
+	if ( PINK == (col_tc = getpixel(terrain, centre, top)) )
+		col_tc = getpixel(env.sky, centre, top);
+	if ( PINK == (col_mc = getpixel(terrain, centre, middle)) )
+		col_mc = getpixel(env.sky, centre, middle);
+	if ( PINK == (col_bc = getpixel(terrain, centre, bottom)) )
+		col_bc = getpixel(env.sky, centre, bottom);
+
+	/*----------------------
+	  --- Right side ---
+	----------------------*/
+	if ( PINK == (col_tr = getpixel(terrain, right, top)) )
+		col_tr = getpixel(env.sky, right, top);
+	if ( PINK == (col_mr = getpixel(terrain, right, middle)) )
+		col_mr = getpixel(env.sky, right, middle);
+	if ( PINK == (col_br = getpixel(terrain, right, bottom)) )
+		col_br = getpixel(env.sky, right, bottom);
+
+
+	// Fetch the rgb parts, according to movement:
+
+	/* --- X-Movement --- */
+	if (mv_left) {
+		// Movement to the left, weight left side colour twice
+		r += (GET_R(col_tl) + GET_R(col_ml) + GET_R(col_bl)) * 2;
+		g += (GET_G(col_tl) + GET_G(col_ml) + GET_G(col_bl)) * 2;
+		b += (GET_B(col_tl) + GET_B(col_ml) + GET_B(col_bl)) * 2;
+		// The others are counted once
+		r += GET_R(col_tc) + GET_R(col_mc) + GET_R(col_bc)
+		   + GET_R(col_tr) + GET_R(col_mr) + GET_R(col_br);
+		g += GET_G(col_tc) + GET_G(col_mc) + GET_G(col_bc)
+		   + GET_G(col_tr) + GET_G(col_mr) + GET_G(col_br);
+		b += GET_B(col_tc) + GET_B(col_mc) + GET_B(col_bc)
+		   + GET_B(col_tr) + GET_B(col_mr) + GET_B(col_br);
+	} else if (mv_right) {
+		// Movement to the right, weight right side colour twice
+		r += (GET_R(col_tr) + GET_R(col_mr) + GET_R(col_br)) * 2;
+		g += (GET_G(col_tr) + GET_G(col_mr) + GET_G(col_br)) * 2;
+		b += (GET_B(col_tr) + GET_B(col_mr) + GET_B(col_br)) * 2;
+		// The others are counted once
+		r += GET_R(col_tc) + GET_R(col_mc) + GET_R(col_bc)
+		   + GET_R(col_tl) + GET_R(col_ml) + GET_R(col_bl);
+		g += GET_G(col_tc) + GET_G(col_mc) + GET_G(col_bc)
+		   + GET_G(col_tl) + GET_G(col_ml) + GET_G(col_bl);
+		b += GET_B(col_tc) + GET_B(col_mc) + GET_B(col_bc)
+		   + GET_B(col_tl) + GET_B(col_ml) + GET_B(col_bl);
+	} else {
+		// No x-movement, weight centre colour twice
+		r += (GET_R(col_tc) + GET_R(col_mc) + GET_R(col_bc)) * 2;
+		g += (GET_G(col_tc) + GET_G(col_mc) + GET_G(col_bc)) * 2;
+		b += (GET_B(col_tc) + GET_B(col_mc) + GET_B(col_bc)) * 2;
+		// The others are counted once
+		r += GET_R(col_tl) + GET_R(col_ml) + GET_R(col_bl)
+		   + GET_R(col_tr) + GET_R(col_mr) + GET_R(col_br);
+		g += GET_G(col_tl) + GET_G(col_ml) + GET_G(col_bl)
+		   + GET_G(col_tr) + GET_G(col_mr) + GET_G(col_br);
+		b += GET_B(col_tl) + GET_B(col_ml) + GET_B(col_bl)
+		   + GET_B(col_tr) + GET_B(col_mr) + GET_B(col_br);
+	}
+
+	/* --- Y-Movement --- */
+	if (mv_up) {
+		// Movement upwards, weight top side colour twice
+		r += (GET_R(col_tl) + GET_R(col_tc) + GET_R(col_tr)) * 2;
+		g += (GET_G(col_tl) + GET_G(col_tc) + GET_G(col_tr)) * 2;
+		b += (GET_B(col_tl) + GET_B(col_tc) + GET_B(col_tr)) * 2;
+		// The others are counted once
+		r += GET_R(col_ml) + GET_R(col_mc) + GET_R(col_mr)
+		   + GET_R(col_bl) + GET_R(col_bc) + GET_R(col_br);
+		g += GET_G(col_ml) + GET_G(col_mc) + GET_G(col_mr)
+		   + GET_G(col_bl) + GET_G(col_bc) + GET_G(col_br);
+		b += GET_B(col_ml) + GET_B(col_mc) + GET_B(col_mr)
+		   + GET_B(col_bl) + GET_B(col_bc) + GET_B(col_br);
+	} else if (mv_down) {
+		// Movement downwards, weight bottom side colour twice
+		r += (GET_R(col_bl) + GET_R(col_bc) + GET_R(col_br)) * 2;
+		g += (GET_G(col_bl) + GET_G(col_bc) + GET_G(col_br)) * 2;
+		b += (GET_B(col_bl) + GET_B(col_bc) + GET_B(col_br)) * 2;
+		// The others are counted once
+		r += GET_R(col_ml) + GET_R(col_mc) + GET_R(col_mr)
+		   + GET_R(col_tl) + GET_R(col_tc) + GET_R(col_tr);
+		g += GET_G(col_ml) + GET_G(col_mc) + GET_G(col_mr)
+		   + GET_G(col_tl) + GET_G(col_tc) + GET_G(col_tr);
+		b += GET_B(col_ml) + GET_B(col_mc) + GET_B(col_mr)
+		   + GET_B(col_tl) + GET_B(col_tc) + GET_B(col_tr);
+	} else {
+		// No y-movement, weight middle colour twice
+		r += (GET_R(col_ml) + GET_R(col_mc) + GET_R(col_mr)) * 2;
+		g += (GET_G(col_ml) + GET_G(col_mc) + GET_G(col_mr)) * 2;
+		b += (GET_B(col_ml) + GET_B(col_mc) + GET_B(col_mr)) * 2;
+		// The others are counted once
+		r += GET_R(col_tl) + GET_R(col_tc) + GET_R(col_tr)
+		   + GET_R(col_bl) + GET_R(col_bc) + GET_R(col_br);
+		g += GET_G(col_tl) + GET_G(col_tc) + GET_G(col_tr)
+		   + GET_G(col_bl) + GET_G(col_bc) + GET_G(col_br);
+		b += GET_B(col_tl) + GET_B(col_tc) + GET_B(col_tr)
+		   + GET_B(col_bl) + GET_B(col_bc) + GET_B(col_br);
+	}
+
+
+	/* I know this looks weird, but what we now have is some kind of summed
+	 * matrix, which is always the same:
+	 * Let's assume that xv and yv are both 0.0, so no movement is happening.
+	 * The result is: (In counted times)
+	 * 2|3|2  ( =  7)
+	 * -+-+-
+	 * 3|4|3  ( = 10)
+	 * -+-+-
+	 * 2|3|2  ( =  7)
+	 *          = 24
+	 * And it is always 24, no matter which movement combination you try
+	 */
+
+	r /= 24;
+	g /= 24;
+	b /= 24;
+
+	return makecol(r > 0xff ? 0xff : r,
+	               g > 0xff ? 0xff : g,
+	               b > 0xff ? 0xff : b);
 }
 
-void GLOBALDATA::addPlayer (PLAYER *player)
+
+// Locks global->command for reading, reads value, then unlocks the variable
+// and returns the value.
+int32_t GLOBALDATA::get_command()
 {
-  if (numPlayers < MAXPLAYERS)
-    {
-      players[numPlayers] = player;
-      numPlayers++;
-      if ((int)player->type == HUMAN_PLAYER)
-        {
-          numHumanPlayers++;
-          computerPlayersOnly = FALSE;
-        }
-    }
+	cmdLock.lock();
+	int32_t c = command;
+ 	cmdLock.unlock();
+	return c;
 }
 
-void GLOBALDATA::removePlayer (PLAYER *player)
+
+TANK* GLOBALDATA::get_curr_tank()
 {
-  int fromCount = 0;
-  int toCount = -1;
-
-  if ((int)player->type == HUMAN_PLAYER)
-    {
-      numHumanPlayers--;
-      if (numHumanPlayers == 0)
-        {
-          computerPlayersOnly = TRUE;
-        }
-    }
-
-  while (fromCount < numPlayers)
-    {
-      if (player != players[fromCount])
-        {
-          if ((toCount >= 0) && (fromCount > toCount))
-            {
-              players[toCount]    = players[fromCount];
-              players[fromCount]  = NULL;
-              toCount++;
-            }
-        }
-      else
-        // Position found,1G now move the remaining players down!
-        toCount = fromCount;
-      fromCount++;
-    }
-  numPlayers--;
+	return currTank;
 }
 
-PLAYER *GLOBALDATA::getNextPlayer (int *playerCount)
+
+/** @brief delegate getting a debris item to the debris pool.
+  *
+  * This delegating function, instead of making the debris pool public,
+  * exists as a point where locking, if it becomes necessary, can be
+  * added without having to rewrite a lot of code.
+**/
+sDebrisItem* GLOBALDATA::get_debris_item(int32_t radius)
 {
-  (*playerCount)++;
-  if (*playerCount >= numPlayers)
-    *playerCount = 0;
-  return (players[*playerCount]);
+	return debris_pool->get_item(radius);
 }
 
-PLAYER *GLOBALDATA::createNewPlayer (ENVIRONMENT *env)
+
+TANK* GLOBALDATA::get_next_tank(bool *wrapped_around)
 {
-  PLAYER **reallocatedPlayers;
-  PLAYER *player;
-
-  reallocatedPlayers = (PLAYER**)realloc (allPlayers, sizeof (PLAYER*) * (numPermanentPlayers + 1));
-  if (reallocatedPlayers != NULL)
-    {
-      allPlayers = reallocatedPlayers;
-    }
-  else
-    {
-      perror ( (char *)"atanks.cc: Failed allocating memory for reallocatedPlayers in GLOBALDATA::createNewPlayer");
-      // exit (1);
-    }
-  player = new PLAYER (this, env);
-  if (!player)
-    {
-      perror ( (char *)"globaldata.cc: Failed allocating memory for player in GLOBALDATA::createNewPlayer");
-      // exit (1);
-    }
-  allPlayers[numPermanentPlayers] = player;
-  numPermanentPlayers++;
-
-  return (player);
+	bool    found    = false;
+	int32_t index    = tankindex + 1;
+	int32_t oldindex = tankindex;
+	int32_t wrapped  = 0;
+
+	while (!found && (wrapped < 2)) {
+		if (index >= MAXPLAYERS) {
+			index = 0;
+			*wrapped_around = true;
+			wrapped++;
+		}
+
+		if ( order[index]
+		  && (index != oldindex)
+		  && !order[index]->destroy)
+			found = true;
+		else
+			++index;
+	}
+
+	tankindex = index;
+
+	// If this tank is valid, the currently selected weapon must be checked
+	// first and changed if depleted
+	TANK* next_tank = order[index];
+	if (next_tank && next_tank->player)
+		next_tank->check_weapon();
+
+	// Whatever happened, the status bar needs an update:
+	if (oldindex != index)
+		updateMenu = true;
+
+	return next_tank;
 }
 
-void GLOBALDATA::destroyPlayer (PLAYER *player)
+
+void GLOBALDATA::initialise ()
 {
-  int fromCount = 0;
-  int toCount = 0;
-
-  for (; fromCount < numPermanentPlayers; fromCount++)
-    {
-      if (allPlayers[fromCount] != player)
-        {
-          allPlayers[toCount] = allPlayers[fromCount];
-          toCount++;
-        }
-    }
-  numPermanentPlayers--;
+	clear_objects();
+	numTanks = 0;
+	clear_to_color (canvas, WHITE);
+	clear_to_color (terrain, PINK);
+
+	for (int32_t i = 0; i < env.screenWidth; ++i) {
+		done[i]    = 0;
+		dropTo[i]  = env.screenHeight - 1;
+		fp[i]      = 0;
+	}
 }
 
 
-
-// This function returns the path to the
-// config directory used by Atanks
-char *GLOBALDATA::Get_Config_Path()
+// return true if the dirt reaches into the given box.
+// left/right and top/bottom are determined automatically.
+bool GLOBALDATA::isDirtInBox(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
 {
-  char *my_config_dir;
-  char *homedir;
+	int32_t top = std::max(std::min(y1, y2),
+	                       env.isBoxed ? MENUHEIGHT + 1 : MENUHEIGHT);
+	// Exit early if the box is below the playing area
+	if (top >= env.screenHeight)
+		return false;
+
+	int32_t bottom = std::min(std::max(y1, y2), env.screenHeight - 2);
+	// Exit early if the box is over the playing area
+	if (bottom <= MENUHEIGHT)
+		return false;
+
+	int32_t left   = std::max(std::min(x1, x2), 1);
+	int32_t right  = std::min(std::max(x1, x2), env.screenWidth - 2);
+
+	// If the box is outside the playing area, this loop won't do anything
+	for (int32_t x = left; x <= right; ++x) {
+		if (surface[x].load(ATOMIC_READ) <= bottom)
+			return true;
+	}
+
+	return false;
+}
 
-  // figure out file name
-  homedir = getenv(HOME_DIR);
-  if (! homedir)
-    homedir = (char *)".";
-  my_config_dir = (char *) calloc( strlen(homedir) + 24,
-                                   sizeof(char) );
-  if (! my_config_dir)
-    return NULL;
 
-  sprintf (my_config_dir, "%s/.atanks", homedir);
-  return my_config_dir;
+/// @return true if the close button was pressed
+bool GLOBALDATA::isCloseBtnPressed()
+{
+	cbpLock.lock();
+	bool result = close_button_pressed;
+	cbpLock.unlock();
 
+	return result;
 }
 
 
-
-// This function checks to see if one full second has passed since the
-// last time the function was called.
-// The function returns true if time has passed. The function
-// returns false if time hasn't passed or it was unable to tell
-// how much time has passed.
-bool GLOBALDATA::Check_Time_Changed()
+/** @brief load global data from a file
+  * This method is still present to provide backwards
+  * compatibility with configurations that were saved
+  * before the values were moved to ENVIRONMENT
+**/
+void GLOBALDATA::load_from_file (FILE* file)
 {
-  static time_t last_second = 0;
-  time_t current_second;
+	char  line[ MAX_CONFIG_LINE + 1] = { 0 };
+	char  field[MAX_CONFIG_LINE + 1] = { 0 };
+	char  value[MAX_CONFIG_LINE + 1] = { 0 };
+	char* result                     = nullptr;
+
+	setlocale(LC_NUMERIC, "C");
+
+	// read until we hit line "*GLOBAL*" or "***" or EOF
+	do {
+		result = fgets(line, MAX_CONFIG_LINE, file);
+		if ( !result
+		  || !strncmp(line, "***", 3) )
+			// eof OR end of record
+			return;
+	} while ( strncmp(line, "*GLOBAL*", 8) );
+
+	bool  done = false;
+
+	while (result && !done) {
+		// read a line
+		memset(line, '\0', MAX_CONFIG_LINE);
+		if ( ( result = fgets(line, MAX_CONFIG_LINE, file) ) ) {
+
+			// if we hit end of the record, stop
+			if (! strncmp(line, "***", 3) )
+				return;
+
+			// strip newline character
+			int32_t line_length = strlen(line);
+			while ( line[line_length - 1] == '\n') {
+				line[line_length - 1] = '\0';
+				line_length--;
+			}
+
+			// find equal sign
+			int32_t equal_position = 1;
+			while ( ( equal_position < line_length )
+				 && ( line[equal_position] != '='  ) )
+				equal_position++;
+
+			// make sure the equal sign position is valid
+			if (line[equal_position] != '=')
+				continue; // Go to next line
+
+			// seperate field from value
+			memset(field, '\0', MAX_CONFIG_LINE);
+			memset(value, '\0', MAX_CONFIG_LINE);
+			strncpy(field, line, equal_position);
+			strncpy(value, &( line[equal_position + 1] ), MAX_CONFIG_LINE);
+
+
+			// Values that were moved to ENVIRONMENT:
+			// They are loaded, for compatibility, but the next
+			// save will put them into the correct section anyway.
+			// So these can eventually be removed.
+			if (!strcasecmp(field, "acceleratedai")) {
+				sscanf(value, "%d", &env.skipComputerPlay);
+				if (env.skipComputerPlay > SKIP_HUMANS_DEAD)
+					env.skipComputerPlay = SKIP_HUMANS_DEAD;
+			} else if (!strcasecmp(field, "checkupdates")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.check_for_updates = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "colourtheme") ) {
+				sscanf(value, "%d", &env.colourTheme);
+				if (env.colourTheme < CT_REGULAR) env.colourTheme = CT_REGULAR;
+				if (env.colourTheme > CT_CRISPY)  env.colourTheme = CT_CRISPY;
+			} else if (!strcasecmp(field, "debrislevel") )
+				sscanf(value, "%d", &env.debris_level);
+			else if (!strcasecmp(field, "detailedland")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.detailedLandscape = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "detailedsky")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.detailedSky = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "dither")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.ditherGradients = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "dividemoney") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.divide_money = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "enablesound")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.sound_enabled = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "frames") ) {
+				int32_t new_fps = 0;
+				sscanf(value, "%d", &new_fps);
+				env.set_fps(new_fps);
+			} else if (!strcasecmp(field, "fullscreen"))
+				sscanf(value, "%d", &env.full_screen);
+			else if (!strcasecmp(field, "interest"))
+				sscanf(value, "%lf", &env.interest);
+			else if (!strcasecmp(field, "language") ) {
+				uint32_t stored_lang = 0;
+				sscanf(value, "%u", &stored_lang);
+				env.language = static_cast<eLanguages>(stored_lang);
+			} else if (!strcasecmp(field, "listenport"))
+				sscanf(value, "%d", &env.network_port);
+			else if (!strcasecmp(field, "maxfiretime") )
+				sscanf(value, "%d", &env.maxFireTime);
+			else if (!strcasecmp(field, "networking")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.network_enabled = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "numpermanentplayers"))
+				sscanf(value, "%d", &env.numPermanentPlayers);
+			else if (!strcasecmp(field, "OSMOUSE")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.osMouse = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "playmusic")) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				env.play_music = val > 0 ? true : false;
+			} else if (!strcasecmp(field, "rounds") )
+				sscanf(value, "%u", &env.rounds);
+			else if (!strcasecmp(field, "screenwidth")
+				  && !env.temp_screenWidth) {
+				sscanf(value, "%d", &env.screenWidth);
+				env.halfWidth = env.screenWidth / 2;
+				env.temp_screenWidth = env.screenWidth;
+			} else if (!strcasecmp(field, "screenheight")
+				  && !env.temp_screenHeight) {
+				sscanf(value, "%d", &env.screenHeight);
+				env.halfHeight = env.screenHeight / 2;
+				env.temp_screenHeight = env.screenHeight;
+			}
+			else if (!strcasecmp(field, "scorehitunit"))
+				sscanf(value, "%d", &env.scoreHitUnit);
+			else if (!strcasecmp(field, "scoreselfhit"))
+				sscanf(value, "%d", &env.scoreSelfHit);
+			else if (!strcasecmp(field, "scoreroundwinbonus"))
+				sscanf(value, "%d", &env.scoreRoundWinBonus);
+			else if (!strcasecmp(field, "scoreteamhit"))
+				sscanf(value, "%d", &env.scoreTeamHit);
+			else if (!strcasecmp(field, "scoreunitdestroybonus"))
+				sscanf(value, "%d", &env.scoreUnitDestroyBonus);
+			else if (!strcasecmp(field, "scoreunitselfdestroy"))
+				sscanf(value, "%d", &env.scoreUnitSelfDestroy);
+			else if (!strcasecmp(field, "sellpercent"))
+				sscanf(value, "%lf", &env.sellpercent);
+			else if (!strcasecmp(field, "sounddriver"))
+				sscanf(value, "%d", &env.sound_driver);
+			else if (!strcasecmp(field, "startmoney"))
+				sscanf(value, "%d", &env.startmoney);
+			else if (!strcasecmp(field, "turntype"))
+				sscanf(value, "%d", &env.turntype);
+			else if (!strcasecmp(field, "violentdeath") )
+				sscanf(value, "%d", &env.violent_death);
+		}     // end of read a line properly
+	}     // end of while not done
+}
 
-  current_second = time(NULL);
-  if ( current_second == last_second )
-    return false;
 
-  // time has changed
-  last_second = current_second;
-  return true;
+void GLOBALDATA::lockClass(eClasses class_)
+{
+	objLocks[class_].lock();
 }
 
 
+void GLOBALDATA::lockLand()
+{
+	landLock.lock();
+}
+
 
-/*
- * This function Loads a music file (if there is one available.
- * A pointer to the music file is returned. If no music can
- * be found, then NULL is returned.
-*/
-SAMPLE *GLOBALDATA::Load_Background_Music()
+void GLOBALDATA::make_bgupdate (int32_t x, int32_t y, int32_t w, int32_t h)
 {
-    SAMPLE *my_sample = NULL;;
-    struct dirent *folder_entry;
-
-    // see if we should bother
-    if (! play_music)
-       return NULL;
-
-    // see if we have the music folder open
-    if (! music_dir)
-    {
-        char *buffer = (char *) calloc( strlen(configDir) + 32, sizeof(char) );
-        if (! buffer)
-           return NULL;
-
-        sprintf(buffer, "%s/music", configDir);
-        music_dir = opendir(buffer);
-        free(buffer);
-        if (! music_dir)
-           return NULL;
-    }
-
-    // at this point we should have an open music folder
-    // the music folder is closed by global's deconstructor
-    // search for files ending in .wav
-    folder_entry = readdir(music_dir);
-    while ( (folder_entry) && (! my_sample) )
-    {
-        // we have something, see if it is a wav file
-        if ( strstr(folder_entry->d_name, ".wav") )
-        {
-            char *filename = (char *) calloc( strlen(configDir) + strlen(folder_entry->d_name) + 64, sizeof(char));
-            if (filename)
-            {
-               sprintf(filename, "%s/music/%s", configDir, folder_entry->d_name);
-               my_sample = load_sample(filename);
-               free(filename);
-            }
-        }
-        if (! my_sample)
-           folder_entry = readdir(music_dir);
-    }
-
-    if (! folder_entry)  // hit end of folder
-    {
-       closedir(music_dir);
-       music_dir = NULL;
-    }
-
-    return my_sample;
+	if (lastUpdatesCount >= env.max_screen_updates) {
+		make_fullUpdate();
+		return;
+	}
+
+	assert( (w > 0) && (h > 0) );
+
+	if ( (w > 0) && (h > 0) )
+		addUpdate(x, y, w, h, lastUpdates, lastUpdatesCount);
 }
 
 
+void GLOBALDATA::make_fullUpdate()
+{
+	// Replace Updates with a full screen update:
+	combineUpdates   = false;
+	updateCount      = 0;
+	lastUpdatesCount = 0;
+
+	// They are split into 2 x 2 updates:
+	for (int32_t x = 0; x < 2; ++x) {
+		make_update(  env.halfWidth * x, 0,
+		              env.halfWidth,     env.halfHeight);
+		make_bgupdate(env.halfWidth * x, 0,
+		              env.halfWidth,     env.halfHeight);
+		make_update(  env.halfWidth * x, env.halfHeight,
+		              env.halfWidth,     env.halfHeight);
+		make_bgupdate(env.halfWidth * x, env.halfHeight,
+		              env.halfWidth,     env.halfHeight);
+	}
+
+	combineUpdates   = true;
+}
 
 
-/*
- * This function sets all variables, which get written to the
- * config file, back to their defaults.
- * -- Jesse
- *  */
-void GLOBALDATA::Reset_Options()
+void GLOBALDATA::make_update (int32_t x, int32_t y, int32_t w, int32_t h)
 {
-  ditherGradients = 1;
-  detailedLandscape = 0;
-  detailedSky = 0;
-  interest = 1.25;
-  scoreRoundWinBonus = 10000;
-  scoreHitUnit = 75;
-  scoreUnitDestroyBonus = 5000;
-  scoreUnitSelfDestroy = 0;
-  sellpercent = 0.80;
-  startmoney = 15000;
-  turntype = TURN_RANDOM;
-  skipComputerPlay = SKIP_HUMANS_DEAD;
-  sound = 1.0;
-  screenWidth = DEFAULT_SCREEN_WIDTH;
-  screenHeight = DEFAULT_SCREEN_HEIGHT;
-  os_mouse = 1.0;
-  language = LANGUAGE_ENGLISH;
-  colour_theme = COLOUR_THEME_CRISPY;
-  frames_per_second = FRAMES_PER_SECOND;
-  violent_death = FALSE;
-  max_fire_time = 0.0;
-  divide_money = 0.0;
-  check_for_updates = 1.0;
-  enable_network = 0.0;
-  #ifdef NETWORK
-  listen_port = DEFAULT_LISTEN_PORT;
-  #endif
-  play_music = 1.0;
-} 
+	if (updateCount >= env.max_screen_updates) {
+		make_fullUpdate();
+		return;
+	}
 
+	// These asserts should catch screwed updates that make no sense
+	assert( (h <= env.screenHeight) && (w <= env.screenWidth) );
+	assert( (w > 0) && (h > 0) );
 
+	if ( (h > 0) && (w > 0) )
+		addUpdate(x, y, w, h, updates, updateCount);
+}
 
 
-/*
- * This function loads all sounds from the data folder and saves them
- * in an array.
- * The function returns TRUE on success or FALSE if an error happens.
-*/
-int GLOBALDATA::Load_Sounds()
+void GLOBALDATA::newRound()
 {
-   int file_count = 0, array_size = 10;
-   char *file_name;
-   FILE *my_file;
-   SAMPLE *temp_sample;
-
-   file_name = (char *) calloc( strlen(dataDir) + 128, sizeof(char) );
-   if (! file_name)
-     return FALSE;
-
-   // allocate space for sound samples
-   sounds = (SAMPLE **) calloc(10, sizeof(SAMPLE *) );
-   if (! sounds)
-   {
-       free(file_name);
-       printf("Unable to create sound array.\n");
-       return FALSE;
-   }
-
-   // read from directory
-   sprintf(file_name, "%s/sound/%d.wav", dataDir, file_count);
-   my_file = fopen(file_name, "r");
-   while ( (my_file) && (sounds) )
-   {
-       fclose(my_file);
-       temp_sample = load_sample(file_name);
-       if (! temp_sample )
-         printf("An error occured loading sound file %s\n", file_name);
-       sounds[file_count] = temp_sample;
-       file_count++; 
-   
-       // make sure we have enough memory for more samples
-       if (file_count >= array_size)
-       {
-            array_size += 10;
-            sounds = (SAMPLE**) realloc(sounds, sizeof(SAMPLE*) * (array_size + 1));
-            if (! sounds)
-              printf("We just ran out of memory loading sound files.\n");
-            else
-            {
-              // zero out new memory pointers
-              int counter;
-              for (counter = file_count; counter <= array_size; counter++)
-                 sounds[counter] = NULL;
-            }
-       }
-       sprintf(file_name, "%s/sound/%d.wav", dataDir, file_count);
-       my_file = fopen(file_name, "r");
-   }
-
-   free(file_name);
-   return TRUE;
+	if ( (currentround > 0) && (currentround-- < env.nextCampaignRound) )
+		env.nextCampaignRound -= env.campaign_rounds;
+
+	tankindex          = 0;
+	naturals_activated = 0;
+	combineUpdates     = true;
+
+	// clean all but texts and tanks
+	int32_t class_ = 0;
+	while (class_ < CLASS_COUNT) {
+		if ( (CLASS_FLOATTEXT != class_) && (CLASS_TANK != class_) ) {
+			while (tails[class_])
+				delete tails[class_];
+		}
+		++class_;
+	}
+
+
+	// Re-init land slide
+	for (int32_t i = 0; i < env.screenWidth; ++i) {
+		done[i]    = 2; // Check at once
+		dropTo[i]  = env.screenHeight - 1;
+		fp[i]      = 0;
+	}
+
+	// Init order array
+	for (int32_t i = 0; i < MAXPLAYERS; ++i)
+		order[i] = nullptr;
 }
 
 
-
-/*
- * This function loads all the bitmaps needed by th game.
- * Bitmaps are found in a series of sub-folders under the
- * dataDir. The function returns TRUE on success and FALSE
- * if an error occures.
-*/
-int GLOBALDATA::Load_Bitmaps()
+/// @brief Tell global that the close button was pressed
+void GLOBALDATA::pressCloseButton()
 {
-   int file_group = 0;
-   int array_size, file_count;
-   char *file_name;
-   char sub_folder[64];
-   FILE *my_file;
-   BITMAP *new_bitmap, **bitmap_array;
-
-   file_name = (char *) calloc( strlen(dataDir) + 128, sizeof(char) );
-   if (!file_name)
-      return FALSE;
-
-   while (file_group < 7)
-   {
-     // set the folder we're looking at
-     switch (file_group)
-     {
-        case 0: strcpy(sub_folder, "title"); break;
-        case 1: strcpy(sub_folder, "button"); break;
-        case 2: strcpy(sub_folder, "misc"); break;
-        case 3: strcpy(sub_folder, "missile"); break;
-        case 4: strcpy(sub_folder, "stock"); break;
-        case 5: strcpy(sub_folder, "tank"); break;
-        case 6: strcpy(sub_folder, "tankgun"); break;
-     }
-
-     // set up empty array
-     array_size = 10;
-     bitmap_array = (BITMAP **) calloc(10, sizeof(BITMAP *) );
-     if (! bitmap_array)
-     {
-         printf("Ran out of memory, loading bitmaps.\n");
-         free(file_name);
-         return FALSE;
-     }
-
-     // search for files
-     file_count = 0;
-     sprintf(file_name, "%s/%s/%d.bmp", dataDir, sub_folder, file_count);
-     my_file = fopen(file_name, "r");
-     while ( (my_file) && (bitmap_array) )
-     {
-         fclose(my_file);
-         new_bitmap = load_bitmap(file_name, NULL);
-         if (! new_bitmap)
-           printf("An error occured loading bitmap %s\n", file_name);
-         bitmap_array[file_count] = new_bitmap;
-         file_count++;
-
-         // make sure array is large enough
-         if ( file_count >= array_size)
-         {
-            array_size += 10;
-            bitmap_array = (BITMAP **) realloc(bitmap_array, sizeof(BITMAP *) * (array_size + 1) );
-            if (! bitmap_array)
-               printf("Unable to increase array size while loading bitmaps.\n");
-            else
-            {
-               // clear memory
-               int count;
-               for (count = file_count; count <= array_size; count++)
-                  bitmap_array[count] = NULL;
-            }
-         }
-
-         // get next file
-         sprintf(file_name, "%s/%s/%d.bmp", dataDir, sub_folder, file_count);
-         my_file = fopen(file_name, "r");
-     }
- 
-     // save the new array
-     switch (file_group)
-     {
-        case 0: title = bitmap_array; break;
-        case 1: button = bitmap_array; break;
-        case 2: misc = bitmap_array; break;
-        case 3: missile = bitmap_array; break;
-        case 4: stock = bitmap_array; break;
-        case 5: tank = bitmap_array; break;
-        case 6: tankgun = bitmap_array; break;
-     }
-
-     file_group++;
-   }
-
-   free(file_name);
-   return TRUE;
+	cbpLock.lock();
+	close_button_pressed = true;
+	cbpLock.unlock();
+	set_command(GLOBAL_COMMAND_QUIT);
 }
 
 
-
-// This file loads in extra fonts the game requires.
-// Fonts should be stored in the datafolder. On
-// success the function returns TRUE. When an
-// error occures, it returns FALSE.
-int GLOBALDATA::Load_Fonts()
+void GLOBALDATA::removeObject (vobj_t *object)
 {
-   char *filename;
-
-   filename = (char *) calloc( strlen(dataDir) + 32, sizeof(char));
-   if (!filename)
-     return FALSE;
-
-   sprintf(filename, "%s/unicode.dat", dataDir);
-   unicode = load_font(filename, NULL, NULL);
-   if (! unicode)
-      printf("Unable to load font %s\n", filename);
-   free(filename);
-
-   if (unicode)
-   {
-      Change_Font();
-      return TRUE;
-   }
-   else return FALSE;  
+	if (nullptr == object)
+		return;
+
+	eClasses class_ = object->getClass();
+
+	/// --- 1: Is the list empty? ---
+	if (nullptr == heads[class_])
+		return;
+
+	objLocks[class_].lock();
+
+	/// --- 2: If the object is head, set it anew:
+	if (object == heads[class_])
+		heads[class_] = object->next;
+
+	/// --- 4: If the object is tail, set it anew:
+	if (object == tails[class_])
+		tails[class_] = object->prev;
+
+	/// --- 5: Take it out of the list:
+	if (object->prev)
+		object->prev->next = object->next;
+	if (object->next)
+		object->next->prev = object->prev;
+	object->prev = nullptr;
+	object->next = nullptr;
+
+	objLocks[class_].unlock();
 }
 
 
-// This function selects the font to use. This should be called
-// right after a language change.
-void GLOBALDATA::Change_Font()
+void GLOBALDATA::removeTank(TANK* tank)
 {
-    // FONT *temp_font = font;
-
-    if ( (language == LANGUAGE_RUSSIAN) || (language == LANGUAGE_GERMAN) )
-       font = unicode;
-    else
-       font = regular_font;
-
-    // if (temp_font != font)     // font has changed
-    // {
-       Load_Weapons_Text(this);
-       Load_Text_Files();
-    // }
-}
+	if (nullptr == tank)
+		return;
 
+	for (int32_t i = 0 ; i < MAXPLAYERS ; ++i) {
+		if (tank == order[i])
+			order[i] = nullptr;
+	}
+}
 
 
-void GLOBALDATA::Update_Player_Menu()
+void GLOBALDATA::replace_canvas ()
 {
-   int index;
 
-   for (index = 0; index < numPermanentPlayers; index++)
-   {
-       if (allPlayers[index])
-          allPlayers[index]->initMenuDesc();
-   }
+	for (int32_t i = 0; i < lastUpdatesCount; ++i) {
+		if ((lastUpdates[i].y + lastUpdates[i].h) > MENUHEIGHT) {
+			blit (env.sky, canvas, lastUpdates[i].x, lastUpdates[i].y - MENUHEIGHT,
+			                       lastUpdates[i].x, lastUpdates[i].y,
+			                       lastUpdates[i].w, lastUpdates[i].h);
+			masked_blit (terrain, canvas, lastUpdates[i].x, lastUpdates[i].y,
+			                              lastUpdates[i].x, lastUpdates[i].y,
+			                              lastUpdates[i].w, lastUpdates[i].h);
+		} // End of having an update below the top bar
+	}
+
+	int32_t l = 0;
+	int32_t r = env.screenWidth - 1;
+	int32_t t = MENUHEIGHT;
+	int32_t b = env.screenHeight - 1;
+
+	vline(canvas, l,     t, b, env.wallColour); // Left edge
+	vline(canvas, l + 1, t, b, env.wallColour); // Left edge
+	vline(canvas, r,     t, b, env.wallColour); // right edge
+	vline(canvas, r - 1, t, b, env.wallColour); // right edge
+	hline(canvas, l,     b, r, env.wallColour); // bottom edge
+	if (env.isBoxed)
+		hline(canvas, l, t, r, env.wallColour); // top edge
+
+	lastUpdatesCount = 0;
 }
 
 
+// Set a new command, lock guarded
+void GLOBALDATA::set_command(int32_t cmd)
+{
+	cmdLock.lock();
+	command = cmd;
+	cmdLock.unlock();
+}
 
 
-// This function loads all needed text files, based on
-// language, into memory. If a previous text was loaded, it is
-// removed from memory first.
-int GLOBALDATA::Load_Text_Files()
+void GLOBALDATA::set_curr_tank(TANK* tank_)
 {
-    char *filename;
-    char suffix[32];
-
-    filename = (char *) calloc( strlen(dataDir) + 64, sizeof(char) );
-    if (! filename)
-       return FALSE;
-
-    if (war_quotes) delete war_quotes;
-    if (instructions) delete instructions;
-    if (gloat) delete gloat;
-    if (revenge) delete revenge;
-    if (retaliation) delete retaliation;
-    if (suicide) delete suicide;
-    if (kamikaze) delete kamikaze;
-    if (ingame) delete ingame;
-
-    if (language == LANGUAGE_RUSSIAN)
-       sprintf(filename, "%s/text/war_quotes_ru.txt", dataDir);
-    else if (language == LANGUAGE_SPANISH)
-       sprintf(filename, "%s/text/war_quotes_ES.txt", dataDir);
-    else
-       sprintf(filename, "%s/text/war_quotes.txt", dataDir);
-    war_quotes = new TEXTBLOCK(filename);
-
-     if (language == LANGUAGE_PORTUGUESE)
-        strcpy(suffix, ".pt_BR.txt");
-     else if (language == LANGUAGE_FRENCH)
-        strcpy(suffix, "_fr.txt");
-     else if (language == LANGUAGE_GERMAN)
-        strcpy(suffix, "_de.txt");
-     else if (language == LANGUAGE_SLOVAK)
-        strcpy(suffix, "_sk.txt");
-     else if (language == LANGUAGE_RUSSIAN)
-        strcpy(suffix, "_ru.txt");
-     else if (language == LANGUAGE_SPANISH)
-        strcpy(suffix, "_ES.txt");
-     else if (language == LANGUAGE_ITALIAN)
-        strcpy(suffix, "_it.txt");
-     else
-        strcpy(suffix, ".txt");       // default to english
-
-    sprintf(filename, "%s/text/instr%s", dataDir, suffix);
-    instructions = new TEXTBLOCK(filename);
-
-    sprintf(filename, "%s/text/gloat%s", dataDir, suffix);
-    gloat = new TEXTBLOCK(filename);
-    sprintf(filename, "%s/text/revenge%s", dataDir, suffix);
-    revenge = new TEXTBLOCK(filename);
-    sprintf(filename, "%s/text/retaliation%s", dataDir, suffix);
-    retaliation = new TEXTBLOCK(filename);
-    sprintf(filename, "%s/text/suicide%s", dataDir, suffix);
-    suicide = new TEXTBLOCK(filename);
-    sprintf(filename, "%s/text/kamikaze%s", dataDir, suffix);
-    kamikaze = new TEXTBLOCK(filename);
-    sprintf(filename, "%s/text/ingame%s", dataDir, suffix);
-    ingame = new TEXTBLOCK(filename);
-
-    free(filename);
-    return TRUE;
+	if (tank_ != currTank) {
+		if (currTank)
+			currTank->deactivate();
+		currTank = tank_;
+		if (currTank)
+			currTank->activate();
+	}
 }
 
 
-#ifdef NETWORK
-// This function sends a message to all connected game clients.
-// Returns TRUE on success or FALSE if the message could not be sent
-int GLOBALDATA::Send_To_Clients(char *message)
+/** @brief go through the land and slide what is to be slid and is not locked
+  * Slide land basic control is done using the 'done[]' array.
+  * done[x] == 0 : Nothing to do. All values assumed to be correct.
+  * done[x] == 1 : This column is currently in sliding.
+  * done[x] == 2 : This column is about to be slid, but the base values aren't set.
+  * done[x] == 3 : This column is about to be slid but locked. (Explosion not done)
+**/
+void GLOBALDATA::slideLand()
 {
-   int index;
-   int message_length;
-
-   if (! message) return FALSE;
-   message_length = strlen(message);
-   for (index = 0; index < numPlayers; index++)
-   {
-       if ( (players[index]) && (players[index]->type == NETWORK_CLIENT) )
-          write(players[index]->server_socket, message, message_length);
-   }     // done all players
-   return TRUE;
+	// Opt out soon if no landslide is to be done
+	if ( (SLIDE_NONE      == env.landSlideType)
+	  || (SLIDE_TANK_ONLY == env.landSlideType)
+	  || ( (SLIDE_CARTOON == env.landSlideType)
+		&& (env.time_to_fall > 0) ) )
+		return;
+
+	for (int32_t col = 1; col < (env.screenWidth - 1); ++col) {
+
+		// Skip this column if it is done or locked
+		if (!done[col] || (3 == done[col]))
+			continue;
+
+		// Set base settings if this hasn't happen, yet
+		if (2 == done[col]) {
+			surface[col].store(0, ATOMIC_WRITE);
+			dropTo[col] = env.screenHeight - 1;
+			done[col]   = 1;
+
+			// Calc the top and bottom of the column to slide
+
+			// Find top-most non-PINK pixel
+			int32_t row = MENUHEIGHT + (env.isBoxed ? 1 : 0);
+
+			for ( ;(row < dropTo[col])
+				&& (PINK == getpixel(terrain, col, row));
+				++row) ;
+			surface[col].store(row, ATOMIC_WRITE); // This is the top pixel with all gaps
+
+			// Find bottom-most PINK pixel
+			int32_t top_row = row;
+			for (row = dropTo[col];
+				   (row > top_row)
+				&& (PINK != getpixel(terrain, col, row));
+				--row) ;
+			dropTo[col] = row;
+
+			// Find bottom-most unsupported pixel
+			for ( ;(row >= top_row)
+				&& (PINK == getpixel(terrain, col, row));
+				--row) ;
+
+			// Check whether there is anything to do or not
+			if ((row >= top_row) && (top_row < dropTo[col])) {
+				fp[col]       = row - top_row + 1;
+				velocity[col] = 0; // Not yet
+				done[col]     = 1; // Can be processed
+			}
+
+			// Otherwise this column is done
+			else {
+				if ( !skippingComputerPlay
+				  && (velocity[col] > .5)
+				  && (fp[col] > 1) )
+					play_natural_sound(DIRT_FRAGMENT, col, 64,
+							1000 - (fp[col] * 800 / env.screenHeight));
+				done[col] = 0; // Nothing to do
+				fp[col]   = 0;
+			}
+		} // End of preparations
+
+		// Do the slide if possible
+		if (1 == done[col]) {
+
+			// Only slide if no neighbours are locked
+			bool can_slide = true;
+			for (int32_t j = col - 1; can_slide && (j > 0) ; --j) {
+				if (3 == done[j])
+					can_slide = false;
+				else if (!done[j])
+					j = 0; // no further look needed.
+			}
+			for (int32_t j = col + 1; can_slide && (j < (env.screenWidth - 1)) ; ++j) {
+				if (3 == done[j])
+					can_slide = false;
+				else if (!done[j])
+					j = env.screenWidth; // no further look needed.
+			}
+
+			if (can_slide) {
+				// Do instant first, because only GRAVITY remains
+				// which is the case if cartoon wait time is over.
+				if ( (SLIDE_INSTANT == env.landSlideType) || skippingComputerPlay) {
+					int32_t surf = surface[col].load(ATOMIC_READ);
+					make_bgupdate (col, surf, 1, dropTo[col] - surf + 1);
+					make_update   (col, surf, 1, dropTo[col] - surf + 1);
+					blit (terrain, terrain, col, surf, col,
+					      dropTo[col] - fp[col] + 1, 1, fp[col]);
+					vline(terrain, col, surf, dropTo[col] - fp[col], PINK);
+					velocity[col] = fp[col]; // Or no sound would be played if done
+					done[col]     = 2; // Recheck
+				} else {
+					velocity[col] += env.gravity;
+					dropIncr[col] += velocity[col];
+
+					int32_t dropAdd = ROUND(dropIncr[col]);
+					int32_t max_top = MENUHEIGHT + (env.isBoxed ? 1 : 0);
+
+					if (dropAdd > 0) {
+
+						int32_t top_row = surface[col].load(ATOMIC_READ);
+
+						assert( (top_row >= 0)
+						     && (top_row < terrain->h)
+						     && "ERROR: top_row out of range!");
+
+						// If the top pixel is not PINK, and the source is not
+						// too high, increase dropAdd:
+						int32_t over_top = top_row - dropAdd;
+						while ( ( over_top <= max_top)
+						     && ( over_top >  0)
+						     && ( PINK != getpixel(terrain, col, over_top) ) ) {
+								++dropAdd;
+								--over_top;
+						}
+
+						if (dropAdd > (dropTo[col] - (top_row + fp[col])) ) {
+							dropAdd       = static_cast<int32_t>(dropTo[col]
+							                               - (top_row + fp[col])
+							                               + 1);
+							dropIncr[col] = dropAdd;
+							done[col]     = 2; // Recheck
+							over_top      = top_row - dropAdd;
+						}
+
+						int32_t slide_height = fp[col] + dropAdd;
+
+						assert( (over_top >= 0)
+						     && (over_top < terrain->h)
+						     && "ERROR: top_row - dropAdd out of range!");
+						assert( (slide_height > 0)
+						     && (slide_height <= terrain->h)
+						     && "ERROR: slide_height out of range!");
+						assert( (  (over_top + slide_height) <= terrain->h)
+						     && "ERROR: over_top + slide_height is out of range!");
+						assert( (  (top_row + slide_height) <= terrain->h)
+						     && "ERROR: over_top + slide_height is out of range!");
+
+						blit (terrain, terrain,
+								col, over_top,
+								col, top_row, 1,
+								slide_height);
+						make_bgupdate(col, over_top, 1,
+						              slide_height + dropAdd + 1);
+						make_update  (col, over_top, 1,
+						              slide_height + dropAdd + 1);
+						// If the top row reaches to the ceiling, there might
+						// not be a PINK pixel to blit. In that case, one has
+						// to be painted "by hand", or the slide will produce
+						// nice long columns. (Happens with dirt balls when
+						// "fixed" under the menubar.
+						if (over_top <= max_top) {
+							putpixel(terrain, col, max_top,     PINK);
+							putpixel(terrain, col, max_top + 1, PINK);
+						}
+
+						surface[col].fetch_add(dropAdd);
+						dropIncr[col] -= dropAdd;
+					}
+				}
+			}
+		} // End of actual slide
+	} // End of looping columns
 }
-#endif
-
 
 
-// This function tries to figure out where the dataDir is. It
-// first checks the current working directory, ".". If the
-// proper files are not found, then we try the defined value of
-// DATA_DIR.
-// On success, TRUE is returned. If no usable directory is
-// found, then FALSE is returned.
-int GLOBALDATA::Find_Data_Dir()
+void GLOBALDATA::unlockClass(eClasses class_)
 {
-     char *current_dir = NULL;
-     FILE *my_phile;
-
-     current_dir = (char *) calloc( strlen(DATA_DIR) + 32, sizeof(char));
-     if (! current_dir)
-        return FALSE;
-
-     // try current dir
-     strcpy(current_dir, "./unicode.dat");   // local dir
-     my_phile = fopen(current_dir, "r");
-     if (my_phile)
-     {
-         fclose(my_phile);
-         free(current_dir);
-         dataDir = ".";
-         return TRUE;
-     }
-
-    // try system dir
-    sprintf(current_dir, "%s/unicode.dat", DATA_DIR);
-    my_phile = fopen(current_dir, "r");
-    if (my_phile)
-    {
-        fclose(my_phile);
-        free(current_dir);
-        dataDir = DATA_DIR;
-        return TRUE;
-    }
-
-    dataDir = DATA_DIR;    // fall back
-    free(current_dir);
-    return FALSE;
+	objLocks[class_].unlock();
 }
 
 
+void GLOBALDATA::unlockLand()
+{
+	landLock.unlock();
+}
 
-/*
-Find the max velocity of a missile
-*/
-double GLOBALDATA::Calc_Max_Velocity()
+
+/// @brief goes through the columns from @a left to @a right and unlocks what is locked.
+void GLOBALDATA::unlockLandSlide(int32_t left, int32_t right)
 {
-   dMaxVelocity = (double) MAX_POWER * (100.0 / (double) frames_per_second) / 100.0;
-   return dMaxVelocity; 
+	// Opt out soon if no landslide is to be done
+	if ( (SLIDE_NONE      == env.landSlideType)
+	  || (SLIDE_TANK_ONLY == env.landSlideType) )
+		return;
+
+	int32_t minX = std::min(left, right);
+	int32_t maxX = std::max(left, right);
+
+	if (minX < 1)
+		minX = 1;
+	if (maxX > (env.screenWidth - 1) )
+		maxX =  env.screenWidth - 1;
+
+	for (int32_t col = minX; col <= maxX; ++col) {
+		if ((done[col] > 2) || !done[col])
+			done[col] = 2;
+	}
 }
 
+#ifndef USE_MUTEX_INSTEAD_OF_SPINLOCK
 
+/// === Spin Lock Implementations ===
 
-/* See how many humans and networked players we have */
-int GLOBALDATA::Count_Humans()
+/// @brief Default ctor
+CSpinLock::CSpinLock() :
+	is_destroyed(ATOMIC_VAR_INIT(false))
 {
-     int count;
-     int humans = 0;
-
-     for (count = 0; count < numPlayers; count++)
-     {
-        if (players[count]->type == HUMAN_PLAYER)
-           humans++;
-        else if (players[count]->type == NETWORK_CLIENT)
-           humans++;
-     }
-
-     return humans;
+	lock_flag.clear(); // Done this way, because VC++ can't do it normally.
+	owner_id = std::thread::id();
 }
 
 
-/*
-This function checks to see if there is a single player left standing.
-If there is, that player's index is returned. If there is no winner, then
--1 is returned. If all tanks have been destroyed, then -2 is returned.
-*/
-int GLOBALDATA::Check_For_Winner()
+/// @brief destructor - mark as destroyed, lock and go
+CSpinLock::~CSpinLock()
 {
-    int index = 0, tank_count = 0;
-    int last_alive = -1;
-    
-    while ( (index < numPlayers) && (tank_count < 2) )
-    {
-         if (players[index]->tank)
-         {
-            last_alive = index;
-            tank_count++;
-         }
-         index++;
-    }
-
-    if ( (last_alive >= 0) && (tank_count == 1) )
-       return last_alive;
-    else if (tank_count > 1)
-       return -1;
-    else      // all dead
-       return -2;
+	std::thread::id this_id = std::this_thread::get_id();
+	bool need_lock = (owner_id != this_id);
+
+	if (need_lock)
+		lock();
+	is_destroyed.store(true);
+	if (need_lock)
+		unlock();
 }
 
 
-/*
-This function gives credits, score and money to the winner(s).
-*/
-void GLOBALDATA::Credit_Winners(int winner)
+/// @brief return true if this thread has an active lock
+bool CSpinLock::hasLock()
+{
+	// This works, because unlock() sets the owner_id to -1.
+	return (std::this_thread::get_id() == owner_id);
+}
+
+
+/** @brief Get a lock
+  * Warning: No recursive locking possible! Only lock once!
+**/
+void CSpinLock::lock()
+{
+	std::thread::id this_id = std::this_thread::get_id();
+	assert( (owner_id != this_id) && "ERROR: Lock already owned!");
+
+	if (false == is_destroyed.load(ATOMIC_READ)) {
+		while (lock_flag.test_and_set()) {
+			std::this_thread::yield();
+		}
+		owner_id = this_id;
+	}
+}
+
+/// @brief unlock if this thread owns the lock. Otherwise do nothing.
+void CSpinLock::unlock()
 {
-   int team_members = 0;
-   int index;
-
-   if (winner < 0)    // no winner
-      return;
-
-   if (winner == JEDI_WIN)
-   {
-      for (index = 0; index < numPlayers; index++)
-      {
-          if (players[index]->team == TEAM_JEDI)
-          {
-             players[index]->score++;
-             players[index]->won++;
-             team_members++;
-          }
-      }
-
-   }
-
-   else if (winner == SITH_WIN)
-   {
-       for (index = 0; index < numPlayers; index++)
-       {
-          if (players[index]->team == TEAM_SITH)
-          {
-             players[index]->score++;
-             players[index]->won++;
-             team_members++;
-          }
-       }
-   }
-   else // credit single winner
-   {
-      players[winner]->score++;
-      players[winner]->won++;
-      players[winner]->money += (int) scoreRoundWinBonus; 
-   }
-
-   // team gets their money too
-   if (team_members)
-   {
-       int team_bonus = (int) scoreRoundWinBonus / team_members;
-       for (index = 0; index < numPlayers; index++)
-       {
-           if ( ((winner == JEDI_WIN) && (players[index]->team == TEAM_JEDI) ) ||
-                ((winner == SITH_WIN) && (players[index]->team == TEAM_SITH) ) )
-                players[index]->money += team_bonus;
-       }
-   }
+	std::thread::id this_id = std::this_thread::get_id();
+	assert( (owner_id == this_id) && "ERROR: Lock *NOT* owned!");
+
+	if (owner_id == this_id) {
+		owner_id = std::thread::id();
+		lock_flag.clear(std::memory_order_release);
+	}
 }
 
+#endif // USE_MUTEX_INSTEAD_OF_SPINLOCK
+
diff --git a/src/globaldata.h b/src/globaldata.h
index 020b00b..4a65410 100644
--- a/src/globaldata.h
+++ b/src/globaldata.h
@@ -17,221 +17,229 @@
  * 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.
- * */
-
-#include <sys/types.h>
-#include <dirent.h>
+ *
+ */
 
 #include "main.h"
-#include "text.h"
 
-#define DEFAULT_SCREEN_WIDTH 800
-#define DEFAULT_SCREEN_HEIGHT 600
 
-#define LANGUAGE_ENGLISH 0.0
-#define LANGUAGE_PORTUGUESE 1.0
-#define LANGUAGE_FRENCH 2.0
-#define LANGUAGE_GERMAN 3.0
-#define LANGUAGE_SLOVAK 4.0
-#define LANGUAGE_RUSSIAN 5.0
-#define LANGUAGE_SPANISH 6.0
-#define LANGUAGE_ITALIAN 7.0
+#include <sys/types.h>
+#include <atomic>
+
 
-#define COLOUR_THEME_REGULAR 0.0
-#define COLOUR_THEME_CRISPY 1.0
+#ifndef HAS_DIRENT
+#  if defined(ATANKS_IS_MSVC)
+#    include "extern/dirent.h"
+#  else
+#    include <dirent.h>
+#  endif // Linux
+#  define HAS_DIRENT 1
+#endif //HAS_DIRENT
 
-#define GAME_NAME_LENGTH 64
 
-#define VIOLENT_DEATH_OFF 0
-#define VIOLENT_DEATH_LIGHT 1
-#define VIOLENT_DEATH_MEDIUM 2
-#define VIOLENT_DEATH_HEAVY 3
+#ifdef USE_MUTEX_INSTEAD_OF_SPINLOCK
+#  include <mutex>
+#  define CSpinLock std::mutex
+#endif // USE_MUTEX_INSTEAD_OF_SPINLOCK
 
-#define MAX_INTEREST_AMOUNT 100000
-#define MAX_TEAM_AMOUNT 500000
 
-#define DEMO_WAIT_TIME 60
+#include "text.h"
+#include "globaltypes.h"
+#include "environment.h"
 
-#define SOUND_AUTODETECT 0
-#define SOUND_OSS 1
-#define SOUND_ESD 2
-#define SOUND_ARTS 3
-#define SOUND_ALSA 4
-#define SOUND_JACK 5
 
-#define FULL_SCREEN_EITHER 10.0
-#define FULL_SCREEN_TRUE  1.0
-#define FULL_SCREEN_FALSE 0.0
+extern int32_t BLACK;
 
-#define MAX_AI_TIME 10
 
-#define ALL_SOCKETS -1
+/// Forwards that do not need to be known here
+struct sDebrisItem;
+struct sDebrisPool;
+class PLAYER;
+class TANK;
+class VIRTUAL_OBJECT;
 
 
-enum skipComputerPlayType
+#ifndef USE_MUTEX_INSTEAD_OF_SPINLOCK
+/** @brief minimal spinlock class
+  * It can do nothing but lock and unlock. No recursive locks.
+  * But then it is a lot faster and leaner than mutexes and critical
+  * sections ever can be. ;)
+**/
+class CSpinLock
 {
-  SKIP_NONE, SKIP_HUMANS_DEAD// , SKIP_AUTOPLAY
+public:
+	explicit CSpinLock();
+    ~CSpinLock();
+
+    CSpinLock(const CSpinLock&)            = delete;
+    CSpinLock &operator=(const CSpinLock&) = delete;
+
+	bool hasLock();
+	void lock();
+	void unlock();
+private:
+
+	abool_t          is_destroyed;
+	aflag_t          lock_flag;
+	std::thread::id  owner_id;
 };
+#endif // USE_MUTEX_INSTEAD_OF_SPINLOCK
 
-class PLAYER;
-class TANK;
+
+/** @class GLOBALDATA
+  * @brief Values used globally during a game round.
+  *
+  * This class holds all values and the corresponding functions for everything
+  * that can change during a game round.
+  *
+  * Everything that is fixed during a game round is consolidated in ENVIRONMENT.
+**/
 class GLOBALDATA
-  {
-  private:
-  DIR *music_dir;
-  public:
-    ~GLOBALDATA();
-    int	WHITE, BLACK, PINK;
-    double	slope[360][2];
-
-    char	*dataDir;
-    char    *configDir;
-    BOX	*updates, *lastUpdates, window;
-    int	updateCount, lastUpdatesCount;
-    int	stopwindow;
-    int	command;
-    double  frames_per_second;
-
-    PLAYER	**allPlayers;
-    int	numPermanentPlayers;
-    #ifdef THREADS
-    pthread_rwlock_t* command_lock;
-    #endif
-    void wr_lock_command ();
-    void unlock_command ();
-    int get_command ();
-    PLAYER	**players;
-    int	numPlayers;
-    int	numHumanPlayers;
-    int	computerPlayersOnly;
-    double	skipComputerPlay;	/* options requires doubles - grr */
-    /* It's a lot simpler than having
-     * special cases for each type */
-    int	numTanks;
-    int	maxNumTanks;
-    TANK	*currTank;
-
-    int	updateMenu;
-
-    int	curland, cursky;
-    int get_curland();
-    void unlock_curland();
-    void lock_curland();
-    void destroy_curland_lock();
-    void init_curland_lock();
-    #ifdef THREADS
-    pthread_mutex_t* curland_lock;
-    #endif
-    int	colourDepth;
-    int	screenWidth, screenHeight;
-    int menuBeginY, menuEndY;
-    int	halfWidth, halfHeight;
-    int     width_override, height_override;
-    double  temp_screenWidth, temp_screenHeight;
-    PLAYER *client_player;     // the index we use to know which one is the player on the client side
-    gfxDataStruct	gfxData;
-    // DATAFILE	*SOUND;
-    ENVIRONMENT *env;
-
-    // bool            full_screen;
-    // int		cacheCirclesBG ;	// This is just a flag, so it need only be
-						// 		an integer, not a double 
-    void Reset_Options();
-
-    /* Logically, these three variables should be ints.  However, converting
-    them to ints (or even an enumerated type) would require some rewritting
-    of the options function - and that's a lot of work. 2003.09.05 */
-    /* Hence being double. 2004.01.05 */
-    double ditherGradients;
-    double detailedLandscape;
-    double detailedSky;
-    double os_mouse;          // whether we should use the OS or custom mouse
-    double colour_theme;     // land and sky gradiant theme
-    double sound_driver;
-
-    /* All this money data; couldn't it be moved into some separate data
-    structure or object */
-    /* It could, but it's not a problem */
-    double	startmoney;
-    double	interest;
-    double	scoreHitUnit;
-    double	scoreSelfHit;
-    double	scoreUnitDestroyBonus;
-    double	scoreUnitSelfDestroy;
-    double	scoreRoundWinBonus;
-    double  sellpercent;
-    double  divide_money;
-    double  play_music;
-    double full_screen;
-    char server_name[128], server_port[128];
-
-    /* double? */
-    /* double for options() reasons, no messing about with casting or
-     * special cases. */
-    double	turntype;
-    double	rounds;
-    int     currentround;
-    double  sound;
-    double  language;
-    int     name_above_tank;
-    int     tank_status_colour;
-    char   *tank_status;
-    char    game_name[GAME_NAME_LENGTH];
-    double  load_game;
-    double  campaign_mode;
-    double  violent_death;
-    double  saved_game_index;
-    char    **saved_game_list;
-    double  max_fire_time;
-    bool close_button_pressed;
-    char *update_string;
-    double check_for_updates;
-    bool demo_mode;
-    double enable_network, listen_port;
-    int draw_background;
-    BITMAP **button, **misc, **missile, **stock, **tank, **tankgun, **title;
-    SAMPLE **sounds;
-    SAMPLE *background_music;
-    FONT *unicode, *regular_font;
-    TEXTBLOCK *war_quotes, *instructions, *ingame;
-    TEXTBLOCK *gloat, *revenge, *retaliation, *suicide, *kamikaze;
-    char *client_message;   // message sent from client to main menu
-
-
-    GLOBALDATA ();
-    void	initialise ();
-    int     saveToFile_Text (FILE *file);
-    int     loadFromFile_Text (FILE *file);
-    int     loadFromFile(ifstream &my_file);
-    void	addPlayer (PLAYER *player);
-    void	removePlayer (PLAYER *player);
-    PLAYER	*getNextPlayer (int *playerCount);
-    PLAYER	*createNewPlayer (ENVIRONMENT *env);
-    void	destroyPlayer (PLAYER *player);
-    char    *Get_Config_Path();
-    bool    Check_Time_Changed();      // check to see if one second has passed
-    bool		bIsGameLoaded;
-    bool		bIsBoxed;
-    int			iHumanLessRounds;
-    double	dMaxVelocity;
-    int Load_Sounds();
-    int Load_Bitmaps();
-    SAMPLE *Load_Background_Music();
-    int Load_Fonts();
-    void Change_Font();
-    void Update_Player_Menu();
-    int Load_Text_Files();
-    #ifdef NETWORK
-    int Send_To_Clients(char *message);        // send a short message to all network clients
-    #endif
-#ifdef DEBUG_AIM_SHOW
-    bool bASD;
-#endif
-    int Find_Data_Dir();
-    double Calc_Max_Velocity();
-    int Count_Humans();
-    int Check_For_Winner();    // returns winner index or -1 for no winner
-    void Credit_Winners(int winner);
-  };
+{
+	typedef VIRTUAL_OBJECT vobj_t;
+	typedef sDebrisItem    item_t;
+
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit GLOBALDATA ();
+	~GLOBALDATA();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+	void    addLandSlide      (int32_t left, int32_t right, bool do_lock);
+	void    addObject         (vobj_t* object);
+	bool    areTanksInBox     (int32_t x1, int32_t y1, int32_t x2, int32_t y2);
+	bool    check_time_changed(); // check to see if one second has passed
+	void    clear_objects     ();
+	void    destroy           ();
+	void    do_updates        ();
+	void    first_init        ();
+	void    free_debris_item  (item_t* item);
+	int32_t get_avg_bgcolor   (int32_t x1, int32_t y1, int32_t x2, int32_t y2,
+	                           double xv, double yv);
+	int32_t get_command       ();
+	TANK*   get_curr_tank     ();
+	item_t* get_debris_item   (int32_t radius);
+	TANK*   get_next_tank     (bool* wrapped_around);
+	void    initialise        ();
+	bool    isCloseBtnPressed ();
+	bool    isDirtInBox       (int32_t x1, int32_t y1, int32_t x2, int32_t y2);
+	void    load_from_file    (FILE* file);
+	void    lockClass         (eClasses class_);
+	void    lockLand          ();
+	void    make_bgupdate     (int32_t x, int32_t y, int32_t w, int32_t h);
+	void    make_fullUpdate   ();
+	void    make_update       (int32_t x, int32_t y, int32_t w, int32_t h);
+	void    newRound          ();
+	void    pressCloseButton  ();
+	void    removeObject      (vobj_t* object);
+	void    removeTank        (TANK* tank);
+	void    replace_canvas    ();
+	bool    save_to_file      (FILE* file);
+	void    set_curr_tank     (TANK* tank_);
+	void    set_command       (int32_t cmd);
+	void    slideLand         ();
+	void    unlockClass       (eClasses class_);
+	void    unlockLand        ();
+	void    unlockLandSlide   (int32_t left, int32_t right);
+
+	template<typename Head_T>
+	void    getHeadOfClass    (eClasses class_, Head_T** head_)
+	{
+		if (class_ < CLASS_COUNT) {
+			objLocks[class_].lock();
+			*head_ = static_cast<Head_T*>(heads[class_]);
+			objLocks[class_].unlock();
+		} else
+			*head_ = nullptr;
+	}
+
+
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+	int32_t     AI_clock              = -1;
+	BITMAP*     canvas                = nullptr;
+	const char* client_message        = nullptr; // message sent from client to main menu
+	PLAYER*     client_player         = nullptr; // the index we use to know which one is the player on the client side
+	int32_t     curland               = 0;
+	int32_t     current_drawing_mode  = DRAW_MODE_SOLID;
+	uint32_t    currentround          = 0;
+	int32_t     cursky                = 0;
+	bool        demo_mode             = false;
+	bool        hasTooMuchDeco        = false; // Set to true if the set FPS are too hard to reach.
+	BOX*        lastUpdates           = nullptr;
+	int32_t     lastUpdatesCount      = 0;
+	double      lastwind              = 0.;
+	int32_t     naturals_activated    = 0;
+	int32_t     numTanks              = 0;
+	TANK*       order[MAXPLAYERS];
+	bool        showScoreBoard        = false;
+	bool        skippingComputerPlay  = false;
+	int32_t     stage                 = STAGE_AIM;
+	bool        stopwindow            = false;
+	ai32_t*     surface               = nullptr;
+	char        tank_status[128];
+	int32_t     tank_status_colour    = BLACK;
+	BITMAP*     terrain               = nullptr;
+	bool        updateMenu            = true;
+	BOX*        updates               = nullptr;
+	char*       update_string         = nullptr;
+	int32_t     used_voices           = 0;
+	double      wind                  = 0.;
+
+
+private:
+
+	typedef sDebrisPool debpool_t;
+
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	// Combine make_update and make_bgupdate with safety checks
+	void addUpdate(int32_t x, int32_t y, int32_t w, int32_t h, BOX* target,
+	               int32_t &target_count);
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	bool            close_button_pressed = false;
+	CSpinLock       cbpLock; //[c]lose_[b]utton_[p]ressed
+	CSpinLock       cmdLock;
+	bool            combineUpdates       = true;
+	int32_t         command              = 0;
+	TANK*           currTank             = nullptr;
+	debpool_t*      debris_pool          = nullptr;
+	int8_t*         done                 = nullptr;
+	double*         dropIncr             = nullptr;
+	int32_t*        dropTo               = nullptr;
+	int32_t*        fp                   = nullptr;
+    vobj_t*         heads[CLASS_COUNT];
+	CSpinLock       landLock;
+	CSpinLock       objLocks[CLASS_COUNT];
+    vobj_t*         tails[CLASS_COUNT];
+	int32_t         tankindex            = 0;
+	int32_t         updateCount          = 0;
+	double*         velocity             = nullptr;
+};
+
+#define HAS_GLOBALDATA 1
 
 #endif
diff --git a/src/globals.h b/src/globals.h
index 2f128a8..754ab57 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1,295 +1,76 @@
-#ifndef GLOBALS_DEFINE
-#define GLOBALS_DEFINE
+#ifndef ATANKS_SRC_ATANKS_CPP
+#	error "globals.h must not be included from anywhere but atanks.cpp!"
+#endif // ATANKS_SRC_ATANKS_CPP
 
-/*
- * atanks - obliterate each other with oversize weapons
- * Copyright (C) 2003  Thomas Hudson
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- * */
-
-
-/*
-#include "virtobj.h"
-#include "floattext.h"
-#include "physobj.h"
-#include "tank.h"
-#include "missile.h"
-#include "explosion.h"
-#include "player.h"
-#include "environment.h"
-*/
 #include "globaldata.h"
-/*
-#include "teleport.h"
-#include "decor.h"
-#include "beam.h"
-*/
-using namespace std;
 
-char	*errorMessage;
-int	errorX, errorY;
+// === The two most important things in the game: ;) ===
+GLOBALDATA  global;
+ENVIRONMENT env;
+
+// === Defined colours used everywhere ===
+int32_t BLACK, BLUE, DARK_GREEN, DARK_GREY, DARK_RED, GREY, GREEN,
+        LIGHT_GREEN, LIME_GREEN, ORANGE, PINK, PURPLE, RED, SILVER,
+        TURQUOISE, WHITE, YELLOW;
 
-int	WHITE, BLACK, PINK, RED, GREEN, DARK_GREEN, BLUE, PURPLE, YELLOW;
+// === General values that are globally used ===
+char        buf[100]; // buffer for general use
+const char* errorMessage;
+int32_t     errorX, errorY;
+int32_t	    k, K; // k = key pressed, K = Key Code from k
+int32_t     fi, lx, ly;
 
-int	k;
-int	ctrlUsedUp;
 
-const gradient topbar_gradient[] =
+// === Gradients ===
+gradient topbar_gradient[4] =
 {
-  {{200,200,200,0}, 0.0},
-  {{255,255,255,0}, 0.5},
-  {{128,128,128,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{200,200,200,0}, 0.0},
+	{{255,255,255,0}, 0.5},
+	{{128,128,128,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
-const gradient stuff_bar_gradient[] =
+gradient stuff_bar_gradient[11] =
 {
-  {{  0,120,  0,0}, 0.0},
-  {{ 10,210, 50,0}, 0.1},
-  {{100,150,150,0}, 0.28},
-  {{100,170,170,0}, 0.31},
-  {{200,200,200,0}, 0.33},
-  {{120,150,120,0}, 0.35},
-  {{180,190,180,0}, 0.5},
-  {{210,210,210,0}, 0.55},
-  {{200,220,200,0}, 0.57},
-  {{255,255,255,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{  0,120,  0,0}, 0.0},
+	{{ 10,210, 50,0}, 0.1},
+	{{100,150,150,0}, 0.28},
+	{{100,170,170,0}, 0.31},
+	{{200,200,200,0}, 0.33},
+	{{120,150,120,0}, 0.35},
+	{{180,190,180,0}, 0.5},
+	{{210,210,210,0}, 0.55},
+	{{200,220,200,0}, 0.57},
+	{{255,255,255,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
-const gradient circles_gradient[] =
+gradient circles_gradient[4] =
 {
-  {{100, 75, 50,0}, 0.0},
-  {{  0,100,  0,0}, 0.5},
-  {{255,255,255,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{100, 75, 50,0}, 0.0},
+	{{  0,100,  0,0}, 0.5},
+	{{255,255,255,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Explosion gradient
-const gradient explosion_gradient1[] =
+gradient explosion_gradient1[3] =
 {
-  {{150, 75, 30,0}, 0.0},
-  {{255,255,255,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{150, 75, 30,0}, 0.0},
+	{{255,255,255,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Explosion gradient
-const gradient explosion_gradient2[] =
+gradient explosion_gradient2[3] =
 {
-  {{255,255,102,0}, 0.0}, // ff6
-  {{255,136, 0,0}, 1.0}, // f80
-  {{0,0,0,0}, -1}
+	{{255,255,102,0}, 0.0}, // ff6
+	{{255,136, 0,0}, 1.0}, // f80
+	{{0,0,0,0}, -1}
 };
 
-const gradient * const explosion_gradients[] =
+gradient* explosion_gradients[2] =
 {
   explosion_gradient1,
   explosion_gradient2
 };
-
-/*
-// Sky gradients, first line is top of screen
-const gradient sky_gradient1[] =
-{
-  {{255,255,255,0}, 0.0},
-  {{100,100,100,0}, 0.1},
-  {{ 80,100,100,0}, 0.5},
-  {{0,0,0,0}, -1}
-};
-
-const gradient sky_gradient2[] =
-{
-  {{  0,  0, 40,0}, 0.0},
-  {{ 40, 40,100,0}, 0.5},
-  {{ 80,80,100,0}, 0.75},
-  {{0,0,0,0}, -1}
-};
-
-const gradient sky_gradient3[] =
-{
-  {{240,  0, 40,0}, 0.0},
-  {{140, 40,100,0}, 0.15},
-  {{ 80, 80,100,0}, 0.75},
-  {{0,0,0,0}, -1}
-};
-
-const gradient sky_gradient4[] =
-{
-  {{ 40, 40, 40,0}, 0.0},
-  {{100, 40,100,0}, 0.2},
-  {{ 80, 80,100,0}, 0.75},
-  {{0,0,0,0}, -1}
-};
-
-const gradient sky_gradient5[] =
-{
-  {{  0, 90, 40,0}, 0.0},
-  {{  0,120,100,0}, 0.2},
-  {{ 40,200,100,0}, 0.75},
-  {{0,0,0,0}, -1}
-};
-
-// Sunset
-const gradient sky_gradient6[] =
-{
-  {{ 70,240,240,0}, 0.0},
-  {{ 70,200,200,0}, 0.3},
-  {{ 70,200,160,0}, 0.35},
-  {{255,200, 70,0}, 0.6},
-  {{255,255,128,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// Burning sky
-const gradient sky_gradient7[] =
-{
-  {{ 20, 20, 20,0}, 0.0},
-  {{255,200,  0,0}, 0.08},
-  {{255,255,  0,0}, 0.13},
-  {{ 20, 20, 20,0}, 0.16},
-  {{255,200,  0,0}, 0.5},
-  {{255,255,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// Burning landscape, black skies
-const gradient sky_gradient8[] =
-{
-  {{  0,  0,  0,0}, 0.0},
-  {{100,  0,  0,0}, 0.4},
-  {{255,255,255,0}, 0.8},
-  {{0,0,0,0}, -1}
-};
-
-// Sky gradients, first line is top of screen
-// dark blue to darker blue
-const gradient sky_gradient9[] =
-{
-  {{ 90, 90,255,0}, 0.0},
-  {{ 60, 60,200,0}, 0.3},
-  {{ 30, 30,150,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// dark blue to blue-grey
-const gradient sky_gradient10[] =
-{
-  {{110,110,255,0}, 0.0},
-  {{150,150,255,0}, 0.3},
-  {{200,200,255,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// white to grey-blue to dark blue
-const gradient sky_gradient11[] =
-{
-  {{255,255,255,0}, 0.0},
-  {{200,200,255,0}, 0.3},
-  {{ 80, 80,180,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// simple purple: dark to light
-const gradient sky_gradient12[] =
-{
-  {{133, 33,133,0}, 0.0},
-  {{220,120,220,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// night sky: black to dark purple
-const gradient sky_gradient13[] =
-{
-  {{  0,  0,  0,0}, 0.0},
-  {{ 50,  0, 50,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// Sunset
-const gradient sky_gradient14[] =
-{
-  {{  0,  0,  0,0}, 0.0},
-  {{ 50,  0, 50,0}, 0.1},
-  {{ 90, 20,  0,0}, 0.3},
-  {{180, 50,  0,0}, 0.7},
-  {{255,150,150,0}, 0.9},
-  {{255,255,100,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// Burning sky
-const gradient sky_gradient15[] =
-{
-  {{185, 60, 60,0}, 0.0},
-  {{240,110,110,0}, 0.5},
-  {{255,110,110,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-// Burning landscape, black skies
-const gradient sky_gradient16[] =
-{
-  {{  0,  0,  0,0}, 0.0},
-  {{170, 50, 50,0}, 0.5},
-  {{220,110,110,0}, 1.0},
-  {{0,0,0,0}, -1}
-};
-
-
-const gradient * const sky_gradients[] =
-{
-  sky_gradient1,
-  sky_gradient2,
-  sky_gradient3,
-  sky_gradient4,
-  sky_gradient5,
-  sky_gradient6,
-  sky_gradient7,
-  sky_gradient8,
-  sky_gradient9,
-  sky_gradient10,
-  sky_gradient11,
-  sky_gradient12,
-  sky_gradient13,
-  sky_gradient14,
-  sky_gradient15,
-  sky_gradient16
-};
-*/
-
-int		cclock,
-fps, frames,
-fi, lx, ly;
-#define CLOCK_MAX 10
-
-int		steep=2, mheight=200, mbase=0;
-double		msteep=.2, smooth=.00;
-
-int winner;
-
-char buf[100];
-
-bool quit_right_now;
-
-WEAPON  weapon[WEAPONS];
-WEAPON  naturals[NATURALS];
-ITEM    item[ITEMS];
-
-GLOBALDATA *my_global;
-
-
-#endif
-
diff --git a/src/globaltypes.cpp b/src/globaltypes.cpp
new file mode 100644
index 0000000..3590aad
--- /dev/null
+++ b/src/globaltypes.cpp
@@ -0,0 +1,68 @@
+#include "globaltypes.h"
+
+/** @file globaltypes.cpp
+  * @brief define helper operators for enum rotation
+**/
+
+/// @brief pre-increment for eDataStages
+eDataStage &operator++ (eDataStage &ds)
+{
+	if (DS_DATA == ds)
+		ds = DS_NAME;
+	else if (DS_DESC == ds)
+		ds = DS_DATA;
+	else
+		ds = DS_DESC;
+	return ds;
+}
+
+
+eLanguages &operator+=(eLanguages &lang, int32_t val)
+{
+	int32_t cur = static_cast<int32_t>(lang) + val;
+	if (cur > 0)
+		cur %= EL_LANGUAGE_COUNT;
+	if (cur < 0)
+		cur = EL_LANGUAGE_COUNT - ((-1 * cur) % EL_LANGUAGE_COUNT);
+	lang = static_cast<eLanguages>(cur);
+	return lang;
+}
+
+
+eLanguages &operator-=(eLanguages &lang, int32_t val)
+{
+	return lang += -1 * val;
+}
+
+
+/// @brief pre-increment for the language
+eLanguages &operator++(eLanguages &lang)
+{
+	return lang += 1;
+}
+
+
+/// @brief post-increment for the language type
+eLanguages operator++(eLanguages &lang, int)
+{
+	eLanguages tmp = lang;
+	lang += 1;
+	return tmp;
+}
+
+
+/// @brief pre-decrement for the language
+eLanguages &operator--(eLanguages &lang)
+{
+	return lang += -1;
+}
+
+
+/// @brief post-decrement for the language type
+eLanguages operator--(eLanguages &lang, int)
+{
+	eLanguages tmp = lang;
+	lang += -1;
+	return tmp;
+}
+
diff --git a/src/globaltypes.h b/src/globaltypes.h
new file mode 100644
index 0000000..1650c11
--- /dev/null
+++ b/src/globaltypes.h
@@ -0,0 +1,337 @@
+#pragma once
+#ifndef ATANKS_SRC_GLOBALTYPES_H_INCLUDED
+#define ATANKS_SRC_GLOBALTYPES_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#define DEFAULT_SCREEN_WIDTH     800
+#define DEFAULT_SCREEN_HEIGHT    600
+#define GAMENAMELEN               64
+#define MAX_INTEREST_AMOUNT   100000
+#define MAX_TEAM_AMOUNT       500000
+#define DEMO_WAIT_TIME            60
+#ifdef ATANKS_IS_WINDOWS
+#  define MAX_AI_TIME             30 // DirectDraw is too slow, allow more time
+#else
+#  define MAX_AI_TIME             10 // Standard with anything but windows
+#endif // ATANKS_IS_MSVC
+#define ALL_SOCKETS               -1
+
+// Start enforcing unified integer typing
+#include <cstdint>
+
+// Use atomic types for thread safety where locks are a bad idea
+#include <atomic>
+typedef std::atomic_bool         abool_t;
+typedef std::atomic_flag         aflag_t;
+typedef std::atomic_int_fast32_t ai32_t;
+
+
+/** @file globaltypes.h
+  * @brief Definitions of types relevant to global data.
+**/
+
+
+/** @enum eBackgroundTypes
+  * @brief types for drawing menu background
+**/
+enum eBackgroundTypes
+{
+	BACKGROUND_BLANK  = 0,
+	BACKGROUND_CIRCLE,
+	BACKGROUND_LINE,
+	BACKGROUND_SQUARE,
+	BACKGROUND_COUNT,
+};
+
+
+/** @enum eBoxModes
+  * @brief Whether boxed mode is on, off or random.
+**/
+enum eBoxModes
+{
+	BM_OFF = 0,
+	BM_ON,
+	BM_RANDOM
+};
+
+
+/** @enum eColourTheme
+  * @brief determine which colour theme to use
+**/
+enum eColourTheme
+{
+	CT_REGULAR = 0,
+	CT_CRISPY
+};
+
+
+/** @enum eCOntrol
+  * @brief control results for human and computer control
+**/
+enum eControl
+{
+	CONTROL_NONE  = 0,
+	CONTROL_FIRE  = 101, // Explicitly fire a weapon/item
+	CONTROL_OTHER = 102, // Something else but firing something
+	CONTROL_SKIP  = 201, // Turn on skipping computer play through in game menu
+	CONTROL_QUIT  = 202  // Any means to quit the game
+};
+
+
+
+/** @enum eDataStage
+  * @brief The data stage of the weapons text file loading
+**/
+enum eDataStage
+{
+	DS_NAME = 0,
+	DS_DESC,
+	DS_DATA
+};
+eDataStage &operator++ (eDataStage &ds); // Enable pre-increment
+
+
+/** @enum eFileStage
+  * @brief The file stage of the weapons text file loading
+**/
+enum eFileStage
+{
+	FS_WEAPONS = 0,
+	FS_NATURALS,
+	FS_ITEMS
+};
+
+
+/** @enum eFullScreen
+  * @brief Whether to use full screen or not.
+**/
+enum eFullScreen
+{
+	FULL_SCREEN_EITHER = 0,
+	FULL_SCREEN_TRUE,
+	FULL_SCREEN_FALSE
+};
+
+
+/** @enum eLandscapeTypes
+  * @brief determine the types the landscape can have
+**/
+enum eLandscapeTypes
+{
+	LAND_RANDOM = 0,
+	LAND_CANYONS,
+	LAND_MOUNTAINS,
+	LAND_VALLEYS,
+	LAND_HILLS,
+	LAND_FOOTHILLS,
+	LAND_PLAIN,
+	LAND_NONE
+};
+
+
+/** @enum eLandSlideTypes
+  * @brief determine the kind of land sliding.
+**/
+enum eLandSlideTypes
+{
+	SLIDE_NONE = 0,   // gravity does not exist
+	SLIDE_TANK_ONLY,  // dirt falls, tank does not
+	SLIDE_INSTANT,    // dirt falls without you seeing it
+	SLIDE_GRAVITY,    // normal
+	SLIDE_CARTOON     // gravity is delayed
+};
+
+
+/** @enum eLanguages
+  * @brief Declare the list of supported languages.
+  *
+  * The last item EL_LANGUAGE_COUNT can be used to retrieve the
+  * number of supported languages.
+  *
+  * This enum is sorted in the order the languages should be listed.
+**/
+enum eLanguages
+{
+	EL_ENGLISH = 0,
+	EL_PORTUGUESE,
+	EL_FRENCH,
+	EL_GERMAN,
+	EL_SLOVAK,
+	EL_RUSSIAN,
+	EL_SPANISH,
+	EL_ITALIAN,
+	EL_LANGUAGE_COUNT
+};
+
+// Helper operators to rotate languages:
+eLanguages &operator++(eLanguages &lang);
+eLanguages operator++ (eLanguages &lang, int);
+eLanguages &operator--(eLanguages &lang);
+eLanguages operator-- (eLanguages &lang, int);
+eLanguages &operator+=(eLanguages &lang, int32_t val);
+eLanguages &operator-=(eLanguages &lang, int32_t val);
+
+
+/** @enum eSatelliteLaser
+  * @brief Size of satellite laser
+**/
+enum eSatelliteLaser
+{
+	SL_NONE = 0,
+	SL_WEAK,
+	SL_STRONG,
+	SL_SUPER
+};
+
+
+/** @enum eSaveGameStage
+  * @brief Stages written in a saved game file
+**/
+enum eSaveGameStage
+{
+	SGS_NONE = 0,
+	SGS_GLOBAL,
+	SGS_ENVIRONMENT,
+	SGS_PLAYERS
+};
+
+
+/** @enum eSkipPlayType
+  * @brief How skipping computer play is managed
+**/
+enum eSkipPlayType
+{
+	SKIP_NONE = 0,
+	SKIP_HUMANS_DEAD
+};
+
+
+/** @enum eSoundDriver
+  * @brief determine which sound driver to use
+**/
+enum eSoundDriver
+{
+	SD_AUTODETECT = 0,
+	SD_OSS,
+	SD_ESD,  // Does anybody still use that?
+	SD_ARTS, // Long long deprecated
+	SD_ALSA,
+	SD_JACK
+	// What about PulseAudio?
+};
+
+
+/** @enum eSounds
+  * @brief enum that describes the sound array
+**/
+enum eSounds
+{
+	// === FIRE a weapon / an item ===
+	SND_FIRE_MISS_SML       = 0,
+	SND_FIRE_MISS_MED       = 1,
+	SND_FIRE_MISS_LRG       = 2,
+	SND_FIRE_NUKE           = 3,
+	SND_FIRE_DEATHEAD       = 4,
+    SND_FIRE_LASER          = 5,
+    SND_FIRE_TELEPORT       = 6,
+    SND_FIRE_WIND_FAN       = 7,
+
+	// === EXPLosion of a weapon / an item ===
+	SND_EXPL_MISS_SML       = 10,
+	SND_EXPL_MISS_MED       = 11,
+	SND_EXPL_MISS_LRG       = 12,
+	SND_EXPL_NUKE           = 13,
+	SND_EXPL_DEATHEAD       = 14,
+    SND_EXPL_DIRT_BALL_BOMB = 15,
+    SND_EXPL_SHAPED_CHARGE  = 16,
+    SND_EXPL_WIDE_BOY       = 17,
+    SND_EXPL_CUTTER         = 18,
+    SND_EXPL_NAPALM         = 19,
+    SND_EXPL_NAPALM_BURN    = 20,
+    SND_EXPL_PER_CENT_BOMB  = 21,
+    SND_EXPL_REDUCER        = 22,
+
+	// === NATUral going off ===
+    SND_NATU_THUNDER_SMLMED = 30,
+    SND_NATU_THUNDER_LRG    = 31,
+    SND_NATU_DIRT_FALL      = 32,
+
+	// === INTErface sounds ===
+    SND_INTE_BUTTON_CLICK   = 40,
+
+	// Play BackGround MUSIC
+	SND_BG_MUSIC            = 50,
+	SND_COUNT
+
+};
+
+
+/** @enum eStages
+  * @brief General stages for the game flow.
+**/
+enum eRoundStages
+{
+	STAGE_AIM = 0,
+	STAGE_FIRE,
+	STAGE_SCOREBOARD, // The scoreboard is displayed
+	STAGE_ENDGAME     // All actions have ceased, the round has ended.
+};
+
+
+/** @enum eViolentDeath
+  * @brief Level of automatic violent death option.
+**/
+enum eViolentDeath
+{
+	VD_OFF = 0,
+	VD_LIGHT,
+	VD_MEDIUM,
+	VD_HEAVY
+};
+
+
+/** @enum eWallTypes
+  * @brief All the types the walls can have.
+**/
+enum eWallTypes
+{
+	WALL_RUBBER = 0,
+	WALL_STEEL,
+	WALL_SPRING,
+	WALL_WRAP,
+	WALL_RANDOM
+};
+
+
+/** @enum eWinner
+  * @brief All possible winning sets
+**/
+enum eWinner
+{
+	WINNER_NO_WIN = 101,
+	WINNER_DRAW   = 102,
+	WINNER_JEDI   = 104,
+	WINNER_SITH   = 105
+};
+
+
+#endif // ATANKS_SRC_GLOBALTYPES_H_INCLUDED
+
diff --git a/src/land.cpp b/src/land.cpp
index 2db0c57..6eab1a6 100644
--- a/src/land.cpp
+++ b/src/land.cpp
@@ -1,209 +1,234 @@
 #include "land.h"
 #include "files.h"
+#include "externs.h"
+#include "gameloop.h"
 
-#ifdef THREADS
-#include <pthread.h>
-#endif
 
-// Define how the land will look.
-
-void generate_land (GLOBALDATA *global, ENVIRONMENT *env, BITMAP *land, int xoffset, int yoffset, int heightx)
-{
-  const int land_height = heightx * 5 / 6;
-  double        smoothness = 100;
-  int   octaves = 8;
-  double        lambda = 0.25;
-  double * depthStrip[2];
-  //Ignore updates from other threads by using a temporary variable.
-  int curland_temp=global->get_curland();
-
-  depthStrip[0] = (double *)calloc(global->screenHeight, sizeof(double));
-  depthStrip[1] = (double *)calloc(global->screenHeight, sizeof(double));
-
-  if (!depthStrip[0] || !depthStrip[1])
-    {
-      cerr << "ERROR: Unable to allocate " << (global->screenHeight * 2) << " bytes in generate_land() !!!" << endl;
-    }
-
-  int landType = (env->landType == LANDTYPE_RANDOM)? (rand () % LANDTYPE_PLAIN) + 1 : (int)env->landType;
-
-  switch (landType)
-    {
-    case LANDTYPE_MOUNTAINS:
-      smoothness = 200;
-      octaves = 8;
-      lambda = 0.65;
-      break;
-    case LANDTYPE_CANYONS:
-      smoothness = 50;
-      octaves = 8;
-      lambda = 0.25;
-      break;
-    case LANDTYPE_VALLEYS:
-      smoothness = 200;
-      octaves = 8;
-      lambda = 0.25;
-      break;
-    case LANDTYPE_HILLS:
-      smoothness = 600;
-      octaves = 6;
-      lambda = 0.40;
-      break;
-    case LANDTYPE_FOOTHILLS:
-      smoothness = 1200;
-      octaves = 3;
-      lambda = 0.25;
-      break;
-    case LANDTYPE_PLAIN:
-      smoothness = 4000;
-      octaves = 2;
-      lambda = 0.2;
-      break;
-    case LANDTYPE_NONE:
-      break;
-    default:
-      break;
-    }
-
-  if (global->detailedLandscape)
-    memset (depthStrip[1], 0, global->screenHeight * sizeof (double));
-
- for (int x = 0; x < global->screenWidth; x++)
-    {
-      int depth = 0;
-      if (landType == LANDTYPE_NONE)
-        env->height[x] = 1;
-      else
-        env->height[x] = ((perlin2DPoint (1.0, smoothness, xoffset + x, yoffset, lambda, octaves) + 1) / 2);
-
-      if (global->detailedLandscape)
-        {
-          memcpy (depthStrip[0], depthStrip[1], global->screenHeight * sizeof(double));
-          for (depth = 1; depth < global->screenHeight; depth++)
-            {
-              depthStrip[1][depth] = ((perlin2DPoint (1.0, smoothness, xoffset + x, yoffset + depth, lambda, octaves) + 1) / 2 * land_height - (global->screenHeight - depth));
-              if (depthStrip[1][depth] > env->height[x] * land_height)
-                depthStrip[1][depth] = env->height[x] * land_height;
-            }
-          depthStrip[1][0] = 0;
-          depth = 1;
-        }
+/*****************************************************************************
+Static temp land bitmap for faster land creation
+*****************************************************************************/
+static BITMAP* temp_land = nullptr;
 
-      if (landType == LANDTYPE_NONE)
-        env->height[x] = 1;
-      else
-        env->height[x] *= land_height;
-
-     for (int y = 0; y <= env->height[x]; y++)
-        {
-          double offset = 0;
-          int color = 0;
-          double shade = 0;
-
-          if (global->detailedLandscape)
-            {
-              double bot; // top, 
-              double minBot, maxTop, btdiff, i;
-              double a1, a2, angle;
-              while ((depthStrip[1][depth] <= y) && (depth < global->screenHeight))
-                depth++;
-              bot = (depthStrip[0][depth - 1] + depthStrip[1][depth - 1]) / 2;
-              // top = (depthStrip[0][depth] + depthStrip[1][depth]) / 2;
-              minBot = MIN (depthStrip[0][depth - 1], depthStrip[1][depth - 1]);
-              maxTop = MAX (depthStrip[0][depth], depthStrip[1][depth]);
-              btdiff = maxTop - minBot;
-              i = (y - bot) / btdiff;
-              a1 = atan2 (depthStrip[0][depth - 1] - depthStrip[1][depth - 1], 1.0) * 180 / PI + 180;
-              a2 = atan2 (depthStrip[0][depth] - depthStrip[1][depth], 1.0) * 180 / PI + 180;
-
-              angle = interpolate (a1, a2, i);
-              shade = global->slope[(int)angle][0];
-            }
-
-          if (global->ditherGradients)
-            offset += rand () % 10 - 5;
-
-          if (global->detailedLandscape)
-            offset += (global->screenHeight - depth) * 0.5;
-
-          while (y + offset < 0)
-            offset /= 2;
-          while (y + offset > global->screenHeight)
-            offset /= 2;
-
-          color = gradientColorPoint (land_gradients[curland_temp], env->height[x], y + offset);
-          if (global->detailedLandscape)
-            {
-              float h, s, v;
-              int r, g, b;
-
-              r = getr (color);
-              g = getg (color);
-              b = getb (color);
-              rgb_to_hsv (r, g, b, &h, &s, &v);
-              shade += (double)(rand () % 1000 - 500) * (1.0/10000);
-              if (shade < 0)
-                v += v * shade * 0.5;
-              else
-                v += (1 - v) * shade * 0.5;
-              hsv_to_rgb (h, s, v, &r, &g, &b);
-              color = makecol (r, g, b);
-            }
-
-          #ifdef THREADS
-          solid_mode();
-          #endif
-          putpixel (land, x, global->screenHeight - y, color);
-          #ifdef THREADS
-          drawing_mode(global->env->current_drawing_mode, NULL, 0, 0);
-          #endif
-        }
-    }
 
-  free(depthStrip[0]);
-  free(depthStrip[1]);
 
-}
-
-
-
-#ifdef THREADS
-// This function should be called as a separate thread. Its sole purpose
-// is to generate terrain images in the background. Then pass them
-// to the main program.
-void *Generate_Land_In_Background(void *new_env)
+// Define how the land will look.
+void generate_land (LevelCreator* lcr, int32_t xoffset, int32_t yoffset, int32_t heightx)
 {
-    ENVIRONMENT *env = (ENVIRONMENT *) new_env;
-    GLOBALDATA *global = env->Get_Globaldata();
-    BITMAP *land = NULL;
-    int xoffset;
-
-   while (global->get_command() != GLOBAL_COMMAND_QUIT)
-   {
-      if (! env->get_waiting_terrain())
-      {
-          // The sky thread runs around the same time. We will wait a few seconds
-          LINUX_DREAMLAND;
-          land = create_bitmap(global->screenWidth, global->screenHeight);
-          if (! land)
-          {
-             printf("Memory error while creating land in background.\n");
-          }
-          else
-          {
-              clear_to_color(land, PINK);
-              xoffset = rand();
-              generate_land(global, env, land, xoffset, 0, global->screenHeight);
-          }
-          env->lock(env->waiting_terrain_lock);
-          env->waiting_terrain = land;
-          env->unlock(env->waiting_terrain_lock);
-      }
-      LINUX_SLEEP;        // avoid taxing the CPU
-   }        // while still running program
- 
-   pthread_exit(NULL);
-   return NULL;         // keeps the compiler happy
+	double* depthStrip[2] = { nullptr, nullptr };
+	double  smoothness    = 100.;
+	int32_t octaves       = 8;
+	double  lambda        = .25;
+	int32_t curland       = global.curland;
+	int32_t land_type     = LAND_RANDOM == env.landType
+	                      ? (rand () % LAND_PLAIN) + 1
+	                      : env.landType;
+	int32_t land_height   = heightx / 6 * 5;
+
+	depthStrip[0] = (double *)calloc(env.screenHeight + 1, sizeof(double));
+	depthStrip[1] = (double *)calloc(env.screenHeight + 1, sizeof(double));
+
+	if (!depthStrip[0] || !depthStrip[1]) {
+		cerr << "ERROR: Unable to allocate ";
+		cerr << ( (env.screenHeight + 1) * 2 * sizeof(double));
+		cerr << " bytes in LandGenerator() !" << endl;
+		return;
+	}
+
+	switch (land_type) {
+		case LAND_MOUNTAINS:
+			smoothness = 200;
+			octaves = 8;
+			lambda = 0.65;
+			break;
+		case LAND_CANYONS:
+			smoothness = 50;
+			octaves = 8;
+			lambda = 0.25;
+			break;
+		case LAND_VALLEYS:
+			smoothness = 200;
+			octaves = 8;
+			lambda = 0.25;
+			break;
+		case LAND_HILLS:
+			smoothness = 600;
+			octaves = 6;
+			lambda = 0.40;
+			break;
+		case LAND_FOOTHILLS:
+			smoothness = 1200;
+			octaves = 3;
+			lambda = 0.25;
+			break;
+		case LAND_PLAIN:
+			smoothness = 4000;
+			octaves = 2;
+			lambda = 0.2;
+			break;
+		case LAND_NONE:
+		default:
+		break;
+	}
+
+	temp_land = create_bitmap(env.screenWidth, env.screenHeight);
+	clear_to_color(temp_land, PINK);
+	clear_to_color(global.terrain, PINK);
+
+	for (int32_t x = 0; lcr->can_work() && (x < env.screenWidth); ++x) {
+		int32_t surface = 1;
+
+		// surface[x] will end up being the y coordinate of the
+		// top land pixel. It is not the real height (distance from bottom to
+		// even level) but this coordinate.
+		// This is why the array is called surface (again) and not (no longer)
+		// height.
+
+		if (land_type != LAND_NONE)
+			surface = (1. + perlin2DPoint(1.0, smoothness, xoffset + x, yoffset,
+			                              lambda, octaves) )
+			        / 2. * land_height;
+		global.surface[x].store(surface);
+	} // End of generating surface array
+
+	// If this is a wrapped landcape, smooth out both sides towards their
+	// opposite counterparts
+	if (WALL_WRAP == env.current_wallType) {
+        int32_t length = env.screenWidth / 20; // 5% left + right = 10% overall
+
+        for (int32_t x = 0; lcr->can_work() && (x < length) ; ++x) {
+			// The idea is to compare the strips from left to right with the
+			// points being taken by a ratio greater the nearer to its wall.
+
+			int32_t left  = x;
+			int32_t right = env.screenWidth - (x + 1);
+
+			double  ratio_n = ( static_cast<double>(x)
+			                  / static_cast<double>(length)
+			                  / 2.) + .5;   // [n]ear: 50% at the wall, 100% at length.
+			double  ratio_f = 1. - ratio_n; // [f]ar : 50% at the wall,   0% at length.
+
+			// Get the heights currently set
+			double  old_left_y  = global.surface[left ].load(ATOMIC_READ);
+			double  old_right_y = global.surface[right].load(ATOMIC_READ);
+
+			// Assemble new heights
+			double  new_left_y  = (old_left_y  * ratio_n) + (old_right_y * ratio_f);
+			double  new_right_y = (old_right_y * ratio_n) + (old_left_y  * ratio_f);
+
+			// Now the new surface values can be stored:
+			global.surface[left ].store(ROUND(new_left_y));
+			global.surface[right].store(ROUND(new_right_y));
+        }
+	}
+
+	// Generate detailed depths
+	for (int32_t x = 0; lcr->can_work() && (x < env.screenWidth); ++x) {
+		int32_t depth   = 0;
+		int32_t surface = global.surface[x].load();
+		if (env.detailedLandscape && (LAND_NONE != land_type)) {
+			memcpy (depthStrip[0], depthStrip[1], env.screenHeight * sizeof(double));
+			for (depth = 1; depth < env.screenHeight; depth++) {
+				depthStrip[1][depth] = (1. + perlin2DPoint (1.0, smoothness,
+				                                            xoffset + x,
+				                                            yoffset + depth,
+				                                            lambda, octaves) )
+				                     / 2. * land_height
+				                     - (env.screenHeight - depth);
+				if (depthStrip[1][depth] > surface )
+					depthStrip[1][depth] = surface;
+			}
+			depthStrip[1][0] = 0;
+			depth = 1;
+		}
+
+		// Now generate the height colourization
+		for (int32_t y = 1; lcr->can_work() && (y <= surface); ++y) {
+
+			lcr->yield();
+
+			double        offset = 0;
+			int32_t color  = BLACK;
+			double        shade  = 0;
+
+			if (env.detailedLandscape) {
+				while ( (depth < env.screenHeight)
+				     && (depthStrip[1][depth] <= y) )
+					++depth;
+
+				double bot    = (depthStrip[0][depth - 1]
+				              +  depthStrip[1][depth - 1]) / 2;
+				double minBot = std::min(depthStrip[0][depth - 1],
+				                         depthStrip[1][depth - 1]);
+				double maxTop = std::max(depthStrip[0][depth],
+				                         depthStrip[1][depth]);
+				double btdiff = maxTop - minBot;
+				double i      = (y - bot) / btdiff;
+				double a1     = RAD2DEG(atan2(depthStrip[0][depth - 1]
+				                            - depthStrip[1][depth - 1], 1.0))
+				              + 180.;
+				double a2     = RAD2DEG(atan2(depthStrip[0][depth]
+				                            - depthStrip[1][depth], 1.0))
+				              + 180.;
+				double angle  = interpolate (a1, a2, i);
+				shade = env.slope[(int)angle][0];
+			}
+
+			if (env.ditherGradients)
+				offset += rand () % 10 - 5;
+
+			if (env.detailedLandscape)
+				offset += (env.screenHeight - depth) * 0.5;
+
+			while ( (y + offset) < 0)
+				offset /= 2;
+			while ( (y + offset) > env.screenHeight)
+				offset /= 2;
+
+			color = gradientColorPoint (land_gradients[curland], surface, y + offset);
+
+			if (env.detailedLandscape) {
+				float h, s, v;
+				int32_t r = getr (color);
+				int32_t g = getg (color);
+				int32_t b = getb (color);
+				rgb_to_hsv(r, g, b, &h, &s, &v);
+
+				shade += (double)(rand () % 1000 - 500) * (1.0/10000);
+
+				if (shade < 0)
+					v += v * shade * 0.5;
+				else
+					v += (1 - v) * shade * 0.5;
+
+				hsv_to_rgb (h, s, v, &r, &g, &b);
+				color = makecol (r, g, b);
+			}
+
+			if (lcr->can_work()) {
+				global.lockLand();
+				solid_mode();
+				putpixel (temp_land, x, env.screenHeight - y, color);
+				drawing_mode(global.current_drawing_mode, NULL, 0, 0);
+				global.unlockLand();
+			}
+		} // End of looping y coordinate
+	} // end of looping x coordinate
+
+	// Put temp land onto the real bitmap:
+	global.lockLand();
+	solid_mode();
+	blit(temp_land, global.terrain, 0, 0, 0, 0, env.screenWidth, env.screenHeight);
+	global.unlockLand();
+
+	// clean up
+	if (temp_land)
+		destroy_bitmap(temp_land);
+	temp_land = nullptr;
+
+	if (depthStrip[0])
+		free(depthStrip[0]);
+
+	if (depthStrip[1])
+		free(depthStrip[1]);
 }
-#endif
 
diff --git a/src/land.h b/src/land.h
index 3c9158c..e889022 100644
--- a/src/land.h
+++ b/src/land.h
@@ -1,169 +1,168 @@
 #ifndef LAND_HEADER_FILE__
 #define LAND_HEADER_FILE__
 
-#include "globaldata.h"
-#include "environment.h"
+#include "main.h"
+
+#define LANDS 8
+// The first LANDS are regular, the second are crispy.
 
 const gradient land_gradient1[] =
 {
-  {{ 20, 40, 20,0}, 0.0},
-  {{ 80,100, 80,0}, 0.5},
-  {{ 80,120,100,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 20, 40, 20,0}, 0.0},
+	{{ 80,100, 80,0}, 0.5},
+	{{ 80,120,100,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient2[] =
 {
-  {{100,200,100,0}, 0.0},
-  {{ 80,100, 80,0}, 0.5},
-  {{ 80,120,100,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{100,200,100,0}, 0.0},
+	{{ 80,100, 80,0}, 0.5},
+	{{ 80,120,100,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient3[] =
 {
-  {{200,100,100,0}, 0.0},
-  {{100, 70, 80,0}, 0.5},
-  {{120, 80,100,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{200,100,100,0}, 0.0},
+	{{100, 70, 80,0}, 0.5},
+	{{120, 80,100,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient4[] =
 {
-  {{ 80, 50, 60,0}, 0.0},
-  {{100, 70, 80,0}, 0.25},
-  {{150,120, 80,0}, 0.75},
-  {{200,180,150,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 80, 50, 60,0}, 0.0},
+	{{100, 70, 80,0}, 0.25},
+	{{150,120, 80,0}, 0.75},
+	{{200,180,150,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient5[] =
 {
-  {{ 20, 20, 20,0}, 0.0},
-  {{100,100,100,0}, 0.7},
-  {{250,250,250,0}, 0.75},
-  {{250,250,250,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 20, 20, 20,0}, 0.0},
+	{{100,100,100,0}, 0.7},
+	{{250,250,250,0}, 0.75},
+	{{250,250,250,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient6[] =
 {
-  {{200,180, 70,0}, 0.0},
-  {{100, 90, 30,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{200,180, 70,0}, 0.0},
+	{{100, 90, 30,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient7[] =
 {
-  {{ 50,200,150,0}, 0.0},
-  {{130,120,180,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 50,200,150,0}, 0.0},
+	{{130,120,180,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient8[] =
 {
-  {{  0,  0,  0,0}, 0.0},
-  {{ 50, 40, 50,0}, 0.4},
-  {{100,  0,  0,0}, 0.8},
-  {{0,0,0,0}, -1}
+	{{  0,  0,  0,0}, 0.0},
+	{{ 50, 40, 50,0}, 0.4},
+	{{100,  0,  0,0}, 0.8},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient9[] =
 {
-  {{100,100,100,0}, 0.0},
-  {{ 50, 50, 50,0}, 0.7},
-  {{255,255,255,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{100,100,100,0}, 0.0},
+	{{ 50, 50, 50,0}, 0.7},
+	{{255,255,255,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient10[] =
 {
-  {{100, 50, 50,0}, 0.0},
-  {{ 50,  0,  0,0}, 0.25},
-  {{100, 10, 10,0}, 0.5},
-  {{100,  0,  0,0}, 0.95},
-  {{  0,  0,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{100, 50, 50,0}, 0.0},
+	{{ 50,  0,  0,0}, 0.25},
+	{{100, 10, 10,0}, 0.5},
+	{{100,  0,  0,0}, 0.95},
+	{{  0,  0,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient11[] =
 {
-  {{ 50,100, 50,0}, 0.0},
-  {{  0, 50,  0,0}, 0.7},
-  {{  0,100,  0,0}, 0.95},
-  {{  0,  0,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 50,100, 50,0}, 0.0},
+	{{  0, 50,  0,0}, 0.7},
+	{{  0,100,  0,0}, 0.95},
+	{{  0,  0,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient12[] =
 {
-  {{ 90, 90, 90,0}, 0.0},
-  {{ 30, 30, 30,0}, 0.4},
-  {{ 60, 60, 60,0}, 0.95},
-  {{  0,  0,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 90, 90, 90,0}, 0.0},
+	{{ 30, 30, 30,0}, 0.4},
+	{{ 60, 60, 60,0}, 0.95},
+	{{  0,  0,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient13[] =
 {
-  {{ 90, 90, 90,0}, 0.0},
-  {{  0, 60,  0,0}, 0.95},
-  {{  0,  0,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 90, 90, 90,0}, 0.0},
+	{{  0, 60,  0,0}, 0.95},
+	{{  0,  0,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient14[] =
 {
-  {{  0,175,  0,0}, 0.0},
-  {{  0, 50,  0,0}, 0.95},
-  {{  0,  0,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{  0,175,  0,0}, 0.0},
+	{{  0, 50,  0,0}, 0.95},
+	{{  0,  0,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient15[] =
 {
-  {{100, 50, 50,0}, 0.0},
-  {{ 50,  0,  0,0}, 0.95},
-  {{  0,  0,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{100, 50, 50,0}, 0.0},
+	{{ 50,  0,  0,0}, 0.95},
+	{{  0,  0,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient land_gradient16[] =
 {
-  {{200,  0,  0,0}, 0.0},
-  {{200,200,  0,0}, 0.25},
-  {{  0,200,  0,0}, 0.5},
-  {{  0,  0,200,0}, 0.75},
-  {{200,  0,200,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{200,  0,  0,0}, 0.0},
+	{{200,200,  0,0}, 0.25},
+	{{  0,200,  0,0}, 0.5},
+	{{  0,  0,200,0}, 0.75},
+	{{200,  0,200,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 const gradient * const land_gradients[] =
 {
-  land_gradient1,
-  land_gradient2,
-  land_gradient3,
-  land_gradient4,
-  land_gradient5,
-  land_gradient6,
-  land_gradient7,
-  land_gradient8,
-  land_gradient9,
-  land_gradient10,
-  land_gradient11,
-  land_gradient12,
-  land_gradient13,
-  land_gradient14,
-  land_gradient15,
-  land_gradient16,
-};
-
-
-void generate_land(GLOBALDATA *global, ENVIRONMENT *env, BITMAP *land, int xoffest, int yoffset, int heightx);
-
-#ifdef THREADS
-void *Generate_Land_In_Background(void *new_env);
-#endif
+	land_gradient1,
+	land_gradient2,
+	land_gradient3,
+	land_gradient4,
+	land_gradient5,
+	land_gradient6,
+	land_gradient7,
+	land_gradient8,
+	land_gradient9,
+	land_gradient10,
+	land_gradient11,
+	land_gradient12,
+	land_gradient13,
+	land_gradient14,
+	land_gradient15,
+	land_gradient16
+};
+
+class LevelCreator;
+
+void generate_land(LevelCreator* lcr, int32_t xoffest, int32_t yoffset, int32_t heightx);
 
 #endif
 
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..3f5eed1
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,156 @@
+#include "main.h"
+
+bool operator==(const BOX &lhs, const BOX &rhs)
+{
+	return (&lhs == &rhs)
+		|| ( (lhs.x == rhs.x)
+		  && (lhs.y == rhs.y)
+		  && (lhs.w == rhs.w)
+		  && (lhs.h == rhs.h) );
+}
+
+bool operator!=(const BOX &lhs, const BOX &rhs)
+{
+	return !(lhs == rhs);
+}
+
+BOX::BOX(int32_t x_, int32_t y_, int32_t w_, int32_t h_) :
+	x(x_),
+	y(y_),
+	w(w_),
+	h(h_)
+{ /* nothing to do here */ }
+
+/// @brief normal assignment operator
+BOX &BOX::operator= (const BOX &src)
+{
+	if (&src != this) {
+		x = src.x;
+		y = src.y;
+		w = src.w;
+		h = src.h;
+	}
+	return *this;
+}
+
+/// @brief rvalue reference assignment operator
+BOX &BOX::operator= (const BOX &&src)
+{
+	x = src.x;
+	y = src.y;
+	w = src.w;
+	h = src.h;
+	return *this;
+}
+
+void BOX::set(int32_t x_, int32_t y_, int32_t w_, int32_t h_)
+{
+	x = x_;
+	y = y_;
+	w = w_;
+	h = h_;
+}
+
+
+POINT_t::POINT_t(int32_t x_, int32_t y_) :
+	x(x_),
+	y(y_)
+{ /* nothing to do here */ }
+
+POINT_t &POINT_t::operator=( const POINT_t& src )
+{
+	if (&src != this) {
+		x = src.x;
+		y = src.y;
+	}
+	return *this;
+}
+
+
+// Safe ctor for WEAPON class
+WEAPON::WEAPON()
+{
+	memset(desc, 0, sizeof(char) * MAX_ITEM_DESC_LEN + 1);
+	memset(name, 0, sizeof(char) * MAX_ITEM_NAME_LEN + 1);
+}
+
+
+/// @brief Get the delay or 1 if delay is zero.
+int32_t WEAPON::getDelayDiv() const
+{
+	return delay > 0 ? delay : 1;
+}
+
+
+/// @brief Get the current weapon description
+const char* WEAPON::getDesc() const
+{
+	return desc;
+}
+
+
+/// @brief Get the current weapon name
+const char* WEAPON::getName() const
+{
+	return name;
+}
+
+
+/// @brief Safely set a new weapon description
+void WEAPON::setDesc(const char* desc_)
+{
+	if (strlen(desc_) > MAX_ITEM_DESC_LEN)
+		fprintf(stderr, "Weapon description for \"%s\" truncated! (%d/%lu characters)\n",
+		        name, MAX_ITEM_DESC_LEN, strlen(desc_));
+	strncpy(desc, desc_, MAX_ITEM_DESC_LEN);
+}
+
+
+/// @brief Safely set a new weapon name
+void WEAPON::setName(const char* name_)
+{
+	if (strlen(name_) > MAX_ITEM_NAME_LEN)
+		fprintf(stderr, "Weapon name for \"%s\" truncated! (%d/%lu characters)\n",
+		        name_, MAX_ITEM_NAME_LEN, strlen(name_));
+	strncpy(name, name_, MAX_ITEM_NAME_LEN);
+}
+
+
+// Safe ctor for WEAPON class
+ITEM::ITEM()
+{
+	memset(vals, 0, sizeof(double) * MAX_ITEMVALS);
+	memset(desc, 0, sizeof(char)   * MAX_ITEM_DESC_LEN + 1);
+	memset(name, 0, sizeof(char)   * MAX_ITEM_NAME_LEN + 1);
+}
+
+
+/// @brief Get the current item description
+const char* ITEM::getDesc() const
+{
+	return desc;
+}
+
+/// @brief Get the current item name
+const char* ITEM::getName() const
+{
+	return name;
+}
+
+/// @brief Safely set a new item description
+void ITEM::setDesc(const char* desc_)
+{
+	if (strlen(desc_) > MAX_ITEM_DESC_LEN)
+		fprintf(stderr, "Item description for \"%s\" truncated! (%d/%lu characters)\n",
+				name, MAX_ITEM_DESC_LEN, strlen(desc_));
+	strncpy(desc, desc_, MAX_ITEM_DESC_LEN);
+}
+
+/// @brief Safely set a new item name
+void ITEM::setName(const char* name_)
+{
+	if (strlen(name_) > MAX_ITEM_NAME_LEN)
+		fprintf(stderr, "Item name for \"%s\" truncated! (%d/%lu characters)\n",
+		        name_, MAX_ITEM_NAME_LEN, strlen(name_));
+	strncpy(name, name_, MAX_ITEM_NAME_LEN);
+}
diff --git a/src/main.h b/src/main.h
index 591cdda..1f3fd89 100644
--- a/src/main.h
+++ b/src/main.h
@@ -21,453 +21,574 @@
  * */
 
 #ifndef VERSION
-#define VERSION "6.0"
+#  error "VERSION information is missing. Fix Makefile."
 #endif
 
 #ifdef GENTOO
-#ifndef DATA_DIR
-#define DATA_DIR "/usr/share/games/atanks"
-#endif
-#endif
+#  ifndef DATA_DIR
+#    define DATA_DIR "/usr/share/games/atanks"
+#  endif // DATA_DIR
+#endif // GENTOO
 
 #ifndef BUFFER_SIZE
-#define BUFFER_SIZE 256
+# define BUFFER_SIZE 256
 #endif
 
-#ifndef _UNUSED
-#define _UNUSED __attribute__ ((unused))
-#endif // _UNUSED
-#ifndef _WARNUNUSED
-#define _WARNUNUSED __attribute__ ((warn_unused_result))
-#endif // _WARNUNUSED
 
-#include <allegro.h>
-#ifdef WIN32
-#include <winalleg.h>
-#endif
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <time.h>
-#include "imagedefs.h"
+/// Important: debug.h not only detects which OS/compiler this is,
+/// it puts an important fix with allegro on windows in place.
+/// Therefore it *must* stay before the block including winalleg.h!
+#include "debug.h"
 
-#include <iostream>
-#include <fstream>
 
-#ifdef LINUX
-#include <unistd.h>
-#endif /* LINUX */
+// The windows port does some crazy stuff with int*_t types.
+#if defined(ATANKS_IS_WINDOWS)
+#  include <cstdint>
+#  ifndef ALLEGRO_HAVE_STDINT_H
+#    define ALLEGRO_HAVE_STDINT_H 1
+#  endif // ALLEGRO_HAVE_STDINT_H
+#  if !defined(ATANKS_SRC_ATANKS_CPP)
+#    define ALLEGRO_NO_MAGIC_MAIN
+#  endif // Not called from atanks.cpp
+#endif // Windows build system
 
-#include <string>
+#  include <allegro.h>
 
-#define GAME_SPEED 14000
+#if defined(ATANKS_IS_WINDOWS)
+#  include <winalleg.h>
+#endif // windows
 
-#ifdef LINUX
-#define LINUX_SLEEP usleep(10000)
-#define LINUX_REST usleep(40000)
-#define LINUX_DREAMLAND sleep(5)
-#endif
 
-#ifdef MACOSX
-#define LINUX_SLEEP usleep(10000)
-#define LINUX_REST usleep(40000)
-#define LINUX_DREAMLAND sleep(5)
+// For visual studio some "workarounds" must be put in place
+#if defined(ATANKS_IS_MSVC)
+#  define PATH_MAX MAX_PATH
+   // Needed for M_PIl to show up
+#  if !defined(_USE_MATH_DEFINES)
+#    define _USE_MATH_DEFINES 1
+#  endif
+#  include <cmath>
+#else
+#  include <unistd.h>
+#  include <cmath>
+#endif // Windows versus Linux
+
+
+// Be sure M_PI and M_PIl are set:
+#if !defined(M_PI)
+#  define M_PI 3.14159265358979323846f
+#endif
+#if !defined(M_PIl)
+#  define M_PIl 3.14159265358979323846L
 #endif
 
-#ifdef WIN32
-#include <windows.h>
-#include <winbase.h>
-#define LINUX_SLEEP Sleep(10)
-#define LINUX_REST Sleep(40)
-#define LINUX_DREAMLAND Sleep(500)
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <chrono>
+#include <thread>
+#include <algorithm>
+
+
+#include "globaltypes.h"
+
+
+#define	DONE_IMAGE             11
+#define	FAST_UP_ARROW_IMAGE    12
+#define	UP_ARROW_IMAGE         13
+#define	DOWN_ARROW_IMAGE       14
+#define	FAST_DOWN_ARROW_IMAGE  15
+
+
+#ifndef HAS_DIRENT
+#  if defined(ATANKS_IS_MSVC)
+#    include "extern/dirent.h"
+#  else
+#    include <dirent.h>
+#  endif // Linux
+#  define HAS_DIRENT 1
+#endif // HAS_DIRENT
+
+
+// Some more workarounds to compile using visual studio:
+#if defined(ATANKS_IS_MSVC)
+#  define snprintf         atanks_snprintf
+#  define strncpy(d, s, c) strncpy_s(d, c+1, s, c)
+#  define strncat(d, s, c) strncat_s(d, c+1, s, c)
+#  define sscanf           sscanf_s
+#  define access           _access
+#  define F_OK             02
+#  define R_OK             04
+#  define W_OK             02
+#  define strcasecmp       _stricmp
+#  define strdup           _strdup
+#  define unlink           _unlink
+#  define mkdir            _mkdir
 #endif
 
-using namespace std;
+// Note: See winclock.h why this is necessary
+/// REMOVE_VS12_WORKAROUND
+#if defined(ATANKS_IS_MSVC) && !defined(ATANKS_IS_AT_LEAST_MSVC13)
+#define USLEEP(microseconds_) Sleep(microseconds_ / 1000);
+#define MSLEEP(milliseconds_) Sleep(milliseconds_);
+#else
+#define USLEEP(microseconds_) std::this_thread::sleep_for(std::chrono::microseconds(microseconds_));
+#define MSLEEP(milliseconds_) std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds_));
+#endif // VS12 workaround
+#define LINUX_SLEEP     MSLEEP(10)
+#define LINUX_REST      MSLEEP(40)
+
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::string;
+
 
 // place to save config and save games
-#ifdef WIN32
-#define HOME_DIR "AppData"
-#endif
-#ifdef LINUX
-#define HOME_DIR "HOME"
-#endif
-#ifdef MACOSX
-#define HOME_DIR "HOME"
-#endif
+#if defined(ATANKS_IS_WINDOWS)
+#  define HOME_DIR "AppData"
+#elif defined(ATANKS_IS_LINUX)
+#  define HOME_DIR "HOME"
+#endif // Windows versus Linux
 
 #ifndef	DATA_DIR
-#define DATA_DIR "."
+#  define DATA_DIR "."
 #endif
-#define SCREEN_WIDTH	800
-#define SCREEN_HEIGHT	600
-#define HALF_WIDTH	(SCREEN_WIDTH/2)
-#define HALF_HEIGHT	(SCREEN_HEIGHT/2)
-#define	STUFF_BAR_WIDTH		400
-#define	STUFF_BAR_HEIGHT	35
-
-#define	GFILE_KEY	0x14233241
-
-#define	FRAMES_PER_SECOND	60
-#ifndef PI
-#define PI 3.1415926535897932384626433832795029L
-#endif // PI
-// #define PI 3.14
-#define MAXUPDATES 256 // This one needs to be watched! If the display goes bye bye, reduce the value!
+
 #define MAX_OVERSHOOT 10000
-#define ABSDISTANCE(x1,y1,x2,y2) abs((int)sqrt((long double)pow(((long double)x2) - ((long double)x1), 2) + (long double)pow(((long double)y2) - ((long double)y1), 2)))
-#define FABSDISTANCE(x1,y1,x2,y2) fabs(sqrt((long double)pow(((long double)x2) - ((long double)x1), 2) + (long double)pow(((long double)y2) - ((long double)y1), 2)))
-#define MAXPLAYERS 10
-#define MAX_POWER  2000
-#define MAX_OBJECTS 400
-#define	MAX_ROUNDS 1000
-#define TANKHEIGHT 20
-#define TANKWIDTH 15
-#define GUNLENGTH 16
-#define MENUHEIGHT 30
-#define SKIES 8
-#define LANDS 8
-#define ALL_SKIES 16
-#define ALL_LANDS 16
+
+
+// The nex few are some math helpers that shorten things dramatically.
+#define SIGN(x_arg)   ((x_arg) < 0  ? -1  : 1 )
+#define SIGNd(x_arg)  ((x_arg) < 0. ? -1. : 1.)
+#define ROUND(x_arg)  static_cast<int32_t>( (x_arg) + (SIGNd(x_arg) * .5))
+#define ROUNDu(x_arg) static_cast<uint32_t>((x_arg) + .5)
+
+#define FABSDISTANCE2(x1,y1,x2,y2) \
+	std::sqrt(std::pow(static_cast<double>(x2) - static_cast<double>(x1), 2.) \
+	        + std::pow(static_cast<double>(y2) - static_cast<double>(y1), 2.) )
+#define FABSDISTANCE3(x1, y1, z1, x2, y2, z2) \
+	std::sqrt(std::pow(static_cast<double>(x2) - static_cast<double>(x1), 2.) \
+	        + std::pow(static_cast<double>(y2) - static_cast<double>(y1), 2.) \
+	        + std::pow(static_cast<double>(z2) - static_cast<double>(z1), 2.) )
+
+#define ABSDISTANCE2(x1,y1,x2,y2)       ROUNDu(FABSDISTANCE2(x1,y1,x2,y2))
+#define ABSDISTANCE3(x1,y1,z1,x2,y2,z2) ROUNDu(FABSDISTANCE3(x1,y1,z1,x2,y2,z2))
+
+#define DEG2RAD(degree_) (static_cast<double>(degree_) * M_PIl / 180. )
+#define RAD2DEG(radian_) (static_cast<double>(radian_) * 180.  / M_PIl)
+
+
+/** @brief show or hide the custom mouse cursor
+  *
+  * This macro can either hide the custom mouse cursor when @a where is set
+  * to nullptr, or draw it on @a where, which then must be a pointer to a
+  * BITMAP.
+  *
+  * This macro should be used to hide the custom mouse cursor before doing any
+  * drawing and to place the mouse cursor on @a where once all other drawing is
+  * done.
+  *
+  * If the OS mouse cursor is used, this macro does nothing.
+  *
+  * @param[in] where BITMAP pointer to draw the custom cursor on or nullptr to
+  * hide the custom mouse cursor.
+**/
+#define SHOW_MOUSE(where) { \
+	if (!env.osMouse) { \
+		if (where != nullptr) unscare_mouse(); \
+		else scare_mouse(); \
+		show_mouse(where); \
+		/* Make the neccessary updates */ \
+		if (where != nullptr) { \
+			global.make_update (mouse_x, mouse_y, env.misc[0]->w, env.misc[0]->h); \
+			global.make_update (lx, ly, env.misc[0]->w, env.misc[0]->h); \
+			lx = mouse_x; \
+			ly = mouse_y; \
+		} \
+	} \
+}
+
+#define MAXPLAYERS    10
+#define MAX_POWER   2000
+#define MIN_POWER    100
+#define MAX_ROUNDS 10000
+
+#define MENUHEIGHT 40
+#define BOXED_TOP 41 // This is the highest non-border pixel in boxed mode
 #define	BALLISTICS 52
 #define	BEAMWEAPONS 3
 #define WEAPONS (BALLISTICS + BEAMWEAPONS)
 #define ITEMS 24
 #define THINGS (WEAPONS + ITEMS)
 #define	NATURALS 6
+#define DIRT_FRAGMENT -1
 #define RADII 6
 #define MAXRADIUS 200
 #define BUTTONFRAMES 2
-#define EXPLODEFRAMES 18
-#define	DISPERSEFRAMES 10
-#define	EXPLOSIONFRAMES	(EXPLODEFRAMES + DISPERSEFRAMES)
-#define TANKSAG 10
+
 #define MENUBUTTONS 7
-#define MISSILEFRAMES 1
-#define ACHANGE 256/360
 #define INGAMEBUTTONS 4
-#define MAX_MISSILES 10
 #define SPREAD 10
-#define SHIELDS 6
-#define WEAPONSOUNDS 4
-#define NAME_LENGTH 24
+#define NAME_LEN 24
 #define ADDRESS_LENGTH 16
 
-//Score coeficients
+#define WAIT_AT_END_OF_ROUND 1 // second (enough with the new live score board)
 
-#define SCORE_DESTROY_UNIT_BONUS	5000
-#define SCORE_UNIT_SELF_DESTROY		0
-#define SCORE_HIT_UNIT			50
-#define SCORE_SELF_HIT			0
+#define MAX_ITEM_NAME_LEN 127
+#define MAX_ITEM_DESC_LEN 511
+#define MAX_ITEMS_IN_STOCK 999999
+#define MAX_MONEY_IN_WALLET 1000000000
 
-//Wind
-#define WIND_COEF			0.3
-#define	MAX_WIND			3
+// Use these instead of the (most strict) defaults,
+// But only where timing by memory fences do not matter.
+#define ATOMIC_READ  std::memory_order_acquire
+#define ATOMIC_WRITE std::memory_order_release
 
 
-
-// bitmaps for tanks
-#define NORMAL_TANK 0
-#define CLASSIC_TANK 1     // really 8 in the .dat file
-#define BIGGREY_TANK 2
-#define T34_TANK 3
-#define HEAVY_TANK 4
-#define FUTURE_TANK 5
-#define UFO_TANK 6
-#define SPIDER_TANK 7
-#define BIGFOOT_TANK 8
-#define MINI_TANK 9
+//turns
+enum turnTypes
+{
+	TURN_HIGH = 0,
+	TURN_LOW,
+	TURN_RANDOM,
+	TURN_SIMUL
+};
 
 
-// background images
-#define BACKGROUND_CIRCLE 0
-#define BACKGROUND_LINE 1
-#define BACKGROUND_BLANK 2
+struct POINT_t
+{
+	int32_t x = 0;
+	int32_t y = 0;
 
-#ifdef NEW_GAMELOOP
-#define WAIT_AT_END_OF_ROUND 300
-#else
-#define WAIT_AT_END_OF_ROUND 100
-#endif
+	explicit
+	POINT_t() { }
+	POINT_t(int32_t x_, int32_t y_);
+	POINT_t &operator=( const POINT_t &src );
+};
 
-// time between volly shots
-#define VOLLY_DELAY 50
 
+struct BOX
+{
+	int32_t x = 0;
+	int32_t y = 0;
+	int32_t w = 0;
+	int32_t h = 0;
+
+	explicit
+	BOX () { }
+	BOX (int32_t x_, int32_t y_, int32_t w_, int32_t h_);
+	BOX &operator=(const BOX  &src);
+	BOX &operator=(const BOX &&src);
+
+	void set(int32_t x_, int32_t y_, int32_t w_, int32_t h_);
+};
 
-// defines for teams
-#define TEAM_SITH 0
-#define TEAM_NEUTRAL 1
-#define TEAM_JEDI 2
+// Make the BOX usage easier:
+bool operator==(const BOX &lhs, const BOX &rhs);
+bool operator!=(const BOX &lhs, const BOX &rhs);
 
 
-//turns
-enum turnTypes
+struct gradient
 {
-  TURN_HIGH, TURN_LOW, TURN_RANDOM, TURN_SIMUL
+	RGB color;
+	float point;
 };
 
-class BOX
-  {
-  public:
-    int 	x, y, w, h;
-  };
-
-
-typedef struct gradient_struct
-  {
-    RGB	color;
-    float	point;
-  } gradient;
 
 class WEAPON
-  {
-  public:
-    char		name[128];		//name of weapon
-    char		description[512];
-    int             cost;		//$ :))
-    int		amt;		//number of weapons in one buying package
-    double		mass;
-    double		drag;
-    int		radius;        // of the explosion
-    int		sound;
-    int		etime;
-    int		damage;		//damage power
-    int		eframes;
-    int		picpoint;       // which picture do we show in flight?
-    int		spread;         // number of weapons in the shot
-    int		delay;		// volleys etc.
-    int		noimpact;
-    int		techLevel;
-    int		warhead;	// Is it a warhead?
-    int		numSubmunitions;// Number of submunitions
-    int		submunition;	// The next stage
-    double		impartVelocity;	// Impart velocity 0.0-1.0 to subs
-    int		divergence;	// Total angle for submunition spread
-    double		spreadVariation;// Uniform or random distribution
-    //   0-1.0 (0=uniform, 1.0=random)
-    //   divergence at centre of range
-    double		launchSpeed;	// Speed given to submunitions
-    double		speedVariation;	// Uniform or random speed
-    //   0-1.0 (0=uniform, 1.0=random)
-    //   launchSpeed at centre of range
-    int		countdown;	// Set the countdown to this
-    double		countVariation;	// Uniform or random countdown
-    //   0-1.0 (0=uniform, 1.0=random)
-    //   countdown at centre of range
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit WEAPON();
+
+
+	/* -----------------------------------
+	 * --- Public methods              ---
+	 * -----------------------------------
+	 */
+
+	int32_t     getDelayDiv() const;
+	const char* getDesc()     const;
+	const char* getName()     const;
+
+	void setDesc(const char* desc_);
+	void setName(const char* name_);
+
+
+	/* -----------------------------------
+	 * --- Public members              ---
+	 * -----------------------------------
+	 */
+	int32_t cost            = 0;  //!< $ :))
+	int32_t amt             = 0;  //!< number of weapons in one buying package
+	double  mass            = 0.;
+	double  drag            = 0.;
+	int32_t radius          = 0;  //!< of the explosion
+	int32_t sound           = 0;
+	int32_t etime           = 0;
+	int32_t damage          = 0;  //!< damage power
+	int32_t picpoint        = 0;  //!< which picture do we show in flight?
+	int32_t spread          = 0;  //!< number of weapons in the shot
+	int32_t delay           = 0;  //!< volleys etc.
+	int32_t noimpact        = 0;
+	int32_t techLevel       = 0;
+	int32_t warhead         = 0;  //!< Is it a warhead?
+	int32_t numSubmunitions = 0;  //!< Number of submunitions
+	int32_t submunition     = 0;  //!< The next stage
+	double  impartVelocity  = 0.; //!< Impart velocity 0.0-1.0 to subs
+	int32_t divergence      = 0;  //!< Total angle for submunition spread
+	double  spreadVariation = 0.; //!< Uniform or random distribution
+	                              //!< 0-1.0 (0=uniform, 1.0=random)
+	                              //!< divergence at centre of range
+	double  launchSpeed     = 0.; //!< Speed given to submunitions
+	double  speedVariation  = 0.; //!< Uniform or random speed
+	                              //!< 0-1.0 (0=uniform, 1.0=random)
+	                              //!< launchSpeed at centre of range
+	int32_t countdown       = 0;  //!< Set the countdown to this
+	double  countVariation  = 0.; //!< Uniform or random countdown
+	                              //!< 0-1.0 (0=uniform, 1.0=random)
+	                              //!< countdown at centre of range
+
+
+private:
+
+
+	/* -----------------------------------
+	 * --- Private members             ---
+	 * -----------------------------------
+	 */
+
+	char desc[MAX_ITEM_DESC_LEN + 1];
+	char name[MAX_ITEM_NAME_LEN + 1];
+};
 
 #define	MAX_ITEMVALS	10
 class ITEM
-  {
-  public:
-    char		name[128];
-    char		description[256];
-    int		cost;
-    int		amt;
-    int		selectable;
-    int		techLevel;
-    int		sound;
-    double		vals[MAX_ITEMVALS];
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit ITEM();
+
+
+	/* -----------------------------------
+	 * --- Public methods              ---
+	 * -----------------------------------
+	 */
+
+	const char* getDesc() const;
+	const char* getName() const;
+
+	void setDesc(const char* desc_);
+	void setName(const char* name_);
+
+
+	/* -----------------------------------
+	 * --- Public members              ---
+	 * -----------------------------------
+	 */
+
+	int32_t cost       = 0;
+	int32_t amt        = 0;
+	int32_t selectable = 0;
+	int32_t techLevel  = 0;
+	int32_t sound      = 0;
+	double  vals[MAX_ITEMVALS];
+
+
+private:
+
+
+	/* -----------------------------------
+	 * --- Private members             ---
+	 * -----------------------------------
+	 */
+
+	char desc[MAX_ITEM_DESC_LEN + 1];
+	char name[MAX_ITEM_NAME_LEN + 1];
+};
 
 
 enum shieldVals
 {
-  SHIELD_ENERGY,
-  SHIELD_REPULSION,
-  SHIELD_RED,
-  SHIELD_GREEN,
-  SHIELD_BLUE,
-  SHIELD_THICKNESS
+	SHIELD_ENERGY,
+	SHIELD_REPULSION,
+	SHIELD_RED,
+	SHIELD_GREEN,
+	SHIELD_BLUE,
+	SHIELD_THICKNESS
 };
+
+
 enum selfDestructVals
 {
-  SELFD_TYPE,
-  SELFD_NUMBER
+	SELFD_TYPE = 0,
+	SELFD_NUMBER
 };
 
+
 enum weaponType
 {
-  SML_MIS, MED_MIS, LRG_MIS, SML_NUKE, NUKE, DTH_HEAD,
-  SML_SPREAD, MED_SPREAD, LRG_SPREAD, SUP_SPREAD, DTH_SPREAD, ARMAGEDDON,
-  CHAIN_MISSILE, CHAIN_GUN, JACK_HAMMER,
-  SHAPED_CHARGE, WIDE_BOY, CUTTER,
-  SML_ROLLER, LRG_ROLLER, DTH_ROLLER,
-  SMALL_MIRV,
-  ARMOUR_PIERCING,
-  CLUSTER, SUP_CLUSTER,
-  FUNKY_BOMB, FUNKY_DEATH,
-  FUNKY_BOMBLET, FUNKY_DEATHLET,
-  BOMBLET, SUP_BOMBLET,
-  BURROWER, PENETRATOR,
-  SML_NAPALM, MED_NAPALM, LRG_NAPALM,
-  NAPALM_JELLY,
-  DRILLER,
-  TREMOR, SHOCKWAVE, TECTONIC,
-  RIOT_BOMB, HVY_RIOT_BOMB,
-  RIOT_CHARGE, RIOT_BLAST,
-  DIRT_BALL, LRG_DIRT_BALL, SUP_DIRT_BALL,
-  SMALL_DIRT_SPREAD,
-  CLUSTER_MIRV,
-  PERCENT_BOMB,
-  REDUCER,
-  SML_LAZER, MED_LAZER, LRG_LAZER, 
-  SML_METEOR, MED_METEOR, LRG_METEOR,
-  SML_LIGHTNING, MED_LIGHTNING, LRG_LIGHTNING
+	SML_MIS            =  0,
+	MED_MIS            =  1,
+	LRG_MIS            =  2,
+	SML_NUKE           =  3,
+	NUKE               =  4,
+	DTH_HEAD           =  5,
+	SML_SPREAD         =  6,
+	MED_SPREAD         =  7,
+	LRG_SPREAD         =  8,
+	SUP_SPREAD         =  9,
+	DTH_SPREAD         = 10,
+	ARMAGEDDON         = 11,
+	CHAIN_MISSILE      = 12,
+	CHAIN_GUN          = 13,
+	JACK_HAMMER        = 14,
+	SHAPED_CHARGE      = 15,
+	WIDE_BOY           = 16,
+	CUTTER             = 17,
+	SML_ROLLER         = 18,
+	LRG_ROLLER         = 19,
+	DTH_ROLLER         = 20,
+	SMALL_MIRV         = 21,
+	ARMOUR_PIERCING    = 22,
+	CLUSTER            = 23,
+	SUP_CLUSTER        = 24,
+	FUNKY_BOMB         = 25,
+	FUNKY_DEATH        = 26,
+	FUNKY_BOMBLET      = 27,
+	FUNKY_DEATHLET     = 28,
+	BOMBLET            = 29,
+	SUP_BOMBLET        = 30,
+	BURROWER           = 31,
+	PENETRATOR         = 32,
+	SML_NAPALM         = 33,
+	MED_NAPALM         = 34,
+	LRG_NAPALM         = 35,
+	NAPALM_JELLY       = 36,
+	DRILLER            = 37,
+	TREMOR             = 38,
+	SHOCKWAVE          = 39,
+	TECTONIC           = 40,
+	RIOT_BOMB          = 41,
+	HVY_RIOT_BOMB      = 42,
+	RIOT_CHARGE        = 43,
+	RIOT_BLAST         = 44,
+	DIRT_BALL          = 45,
+	LRG_DIRT_BALL      = 46,
+	SUP_DIRT_BALL      = 47,
+	SMALL_DIRT_SPREAD  = 48,
+	CLUSTER_MIRV       = 49,
+	PERCENT_BOMB       = 50,
+	REDUCER            = 51, // Last ballistic
+	SML_LAZER          = 52,
+	MED_LAZER          = 53,
+	LRG_LAZER          = 54, // Last weapon (WEAPONS == 55)
+	SML_METEOR         = 55,
+	MED_METEOR         = 56,
+	LRG_METEOR         = 57,
+	SML_LIGHTNING      = 58,
+	MED_LIGHTNING      = 59,
+	LRG_LIGHTNING      = 60 // Last natural
 };
 
-// #define LAST_EXPLOSIVE	NAPALM_JELLY
+
 #define LAST_EXPLOSIVE DRILLER
 
-#define ITEM_NO_SHIELD			-1
+#define ITEM_NO_SHIELD   -1
 enum itemType
 {
-  ITEM_TELEPORT,
-  ITEM_SWAPPER,
-  ITEM_MASS_TELEPORT,
-  ITEM_FAN,
-  ITEM_VENGEANCE,
-  ITEM_DYING_WRATH,
-  ITEM_FATAL_FURY,
-  ITEM_LGT_SHIELD,
-  ITEM_MED_SHIELD,
-  ITEM_HVY_SHIELD,
-  ITEM_LGT_REPULSOR_SHIELD,
-  ITEM_MED_REPULSOR_SHIELD,
-  ITEM_HVY_REPULSOR_SHIELD,
-  ITEM_ARMOUR,
-  ITEM_PLASTEEL,
-  ITEM_INTENSITY_AMP,
-  ITEM_VIOLENT_FORCE,
-  ITEM_SLICKP,
-  ITEM_DIMPLEP,
-  ITEM_PARACHUTE,
-  ITEM_REPAIRKIT,
-  ITEM_FUEL,
-  ITEM_ROCKET,
-  ITEM_SDI
+	ITEM_TELEPORT             =  0, // 55 (weap_idx - WEAPONS)
+	ITEM_SWAPPER              =  1, // 56
+	ITEM_MASS_TELEPORT        =  2, // 57
+	ITEM_FAN                  =  3, // 58
+	ITEM_VENGEANCE            =  4, // 59
+	ITEM_DYING_WRATH          =  5, // 60
+	ITEM_FATAL_FURY           =  6, // 61
+	ITEM_LGT_SHIELD           =  7,
+	ITEM_MED_SHIELD           =  8,
+	ITEM_HVY_SHIELD           =  9,
+	ITEM_LGT_REPULSOR_SHIELD  = 10,
+	ITEM_MED_REPULSOR_SHIELD  = 11,
+	ITEM_HVY_REPULSOR_SHIELD  = 12,
+	ITEM_ARMOUR               = 13,
+	ITEM_PLASTEEL             = 14,
+	ITEM_INTENSITY_AMP        = 15,
+	ITEM_VIOLENT_FORCE        = 16,
+	ITEM_SLICKP               = 17,
+	ITEM_DIMPLEP              = 18,
+	ITEM_PARACHUTE            = 19,
+	ITEM_REPAIRKIT            = 20,
+	ITEM_FUEL                 = 21, // 76
+	ITEM_ROCKET               = 22, // 77
+	ITEM_SDI                  = 23 // Last item
 };
 
 #define SHIELD_COUNT		6
 
 //signals
-#define	SIG_QUIT_GAME		-1
-#define SIG_OK			0
-#define GLOBAL_COMMAND_QUIT		-1
-#define GLOBAL_COMMAND_MENU		0
-#define GLOBAL_COMMAND_OPTIONS		 1
-#define GLOBAL_COMMAND_PLAYERS		 2
-#define GLOBAL_COMMAND_CREDITS		 3
-#define	GLOBAL_COMMAND_HELP		     4
-#define GLOBAL_COMMAND_PLAY        5
-#define GLOBAL_COMMAND_DEMO        6
-#define GLOBAL_COMMAND_NETWORK     7
-
-
-// Classes
-#define	ANY_CLASS		-1
-#define	VIRTUAL_OBJECT_CLASS	0
-#define	FLOATTEXT_CLASS		1
-#define	PHYSICAL_OBJECT_CLASS	2
-#define	MISSILE_CLASS		3
-#define	TANK_CLASS		4
-#define	EXPLOSION_CLASS		5
-#define	TELEPORT_CLASS		6
-#define	BEAM_CLASS		7
-#define	DECOR_CLASS		8
-
-
-typedef struct
-  {
-    // DATAFILE	*M, *T, *TITLE, *S, *E, *B, *L,
-    // *TG, *MI, *STOCK_IMAGE;
-
-    BITMAP		*sky_gradient_strips[ALL_SKIES];
-    BITMAP		*land_gradient_strips[ALL_LANDS];
-    // BITMAP		*circle_gradient_strip;
-    BITMAP		*stuff_bar_gradient_strip;
-    BITMAP		*topbar_gradient_strip;
-    BITMAP		*explosion_gradient_strip;
-    BITMAP		*stuff_bar[2];
-    BITMAP		*stuff_icon_base;
-    // BITMAP		*circlesBG;
-    BITMAP		*topbar;
-    BITMAP		*explosions[EXPLOSIONFRAMES];
-    BITMAP		*flameFront[EXPLOSIONFRAMES];
-  } gfxDataStruct;
-
-
-
-class GLOBALDATA;
-class ENVIRONMENT;
-int drawFracture (GLOBALDATA *global, ENVIRONMENT *env, BITMAP *dest, BOX *updateArea, int x, int y, int angle, int width, int segmentLength, int maxRecurse, int recurseDepth);
-int setSlideColumnDimensions (GLOBALDATA *global, ENVIRONMENT *env, int x, bool reset);
-double Noise (int x);
-double Noise2D (int x, int y);
-double interpolate (double x1, double x2, double i);
-double perlin1DPoint (double amplitude, double scale, double xo, double lambda, int octaves);
-double perlin2DPoint (double amplitude, double scale, double xo, double yo, double lambda, int octaves);
-
-void quickChange (GLOBALDATA *global, BITMAP *target) ;
-void change(GLOBALDATA *global, BITMAP *target);
-
-int checkPixelsBetweenTwoPoints (GLOBALDATA *global, ENVIRONMENT *env, double *startX, double *startY, double endX, double endY);
-long int calcTotalPotentialDamage (int weapNum);
-long int calcTotalEffectiveDamage (int weapNum);
-
-void close_button_handler(void);
-
-void doLaunch(GLOBALDATA *gd, ENVIRONMENT *env);
-
-#define GENSKY_DETAILED		1
-#define GENSKY_DITHERGRAD	2
-void generate_sky (GLOBALDATA *global, BITMAP* bmp, const gradient* grad, int flags ) ;
-
-// load all game settings
-bool Load_Game_Settings(GLOBALDATA *global, ENVIRONMENT *env, char *text_file) _WARNUNUSED;
-bool loadPlayers(GLOBALDATA *global, ENVIRONMENT *env, ifstream &ifsFile) _WARNUNUSED;
-// save all game settings
-bool Save_Game_Settings(GLOBALDATA *global, ENVIRONMENT *env, char *text_file, bool bIsSaveGame = false) _WARNUNUSED;
-bool savePlayers (GLOBALDATA *global, ofstream &ofsFile) _WARNUNUSED;
-int Save_Game_Settings_Text(GLOBALDATA *global, ENVIRONMENT *env, char *path_to_file);
-
-// These defines are needed to identify parts of the binary save files.
-#define FILEPART_ENVIRON "[Environment]"
-#define FILEPART_GLOBALS "[Global]"
-#define FILEPART_PLAYERS "[Players]"
-#define FILEPART_ENDSECT "[EndSection]"
-#define FILEPART_ENDPLYR "[EndPlayer]"
-#define FILEPART_ENDFILE "[EndFile]"
-
-// methods to handle loading and saving of data
-
-/*  --- take data out of filestream -- */
-bool popColo(int    &aData, ifstream &ifsFile) _WARNUNUSED;
-bool popData(int    &aData, ifstream &ifsFile) _WARNUNUSED;
-bool popData(double &aData, ifstream &ifsFile) _WARNUNUSED;
-bool popData(char  * aArea, bool &aIsData, ifstream &ifsFile) _WARNUNUSED;
-//bool popData( const char  * aArea, int    &aData, ifstream &ifsFile) _WARNUNUSED;
-//bool popData( const char  * aArea, double &aData, ifstream &ifsFile) _WARNUNUSED;
-//bool popData(char * aArea, double &aData, ifstream &ifsFile) _WARNUNUSED;
-
-/*  --- put data into filestream -- */
-//bool pushData(const int     aData, ofstream &ofsFile) _WARNUNUSED;
-//bool pushData(const double  aData, ofstream &ofsFile) _WARNUNUSED;
-bool pushColo(const char  * aArea, const int    aData, ofstream &ofsFile) _WARNUNUSED;
-bool pushData(const char  * aData, ofstream &ofsFile) _WARNUNUSED;
-bool pushData(const char  * aArea, const int    aData, ofstream &ofsFile) _WARNUNUSED;
-//bool pushName(const char  * aData, ofstream &ofsFile) _WARNUNUSED; // Special function for name saving
-bool pushData(const char  * aArea, const double aData, ofstream &ofsFile) _WARNUNUSED;
-
-/*  --- seek specific data within filestream -- */
-//bool seekData(const int     aData, ifstream &ifsFile) _WARNUNUSED;
-//bool seekData(const char  * aData, ifstream &ifsFile) _WARNUNUSED;
-
-// handle changes to global settings
-int Change_Settings(double old_mouse, double old_sound, double new_mouse, double new_sound, void *mouse_image);
-
-int *Sort_Scores(GLOBALDATA *global);
-int Game_Client(GLOBALDATA *global, ENVIRONMENT *env, int socket_number);
+#define	SIG_QUIT_GAME         -1
+#define SIG_OK                 0
+#define GLOBAL_COMMAND_QUIT   -1
+#define GLOBAL_COMMAND_MENU    0
+#define GLOBAL_COMMAND_OPTIONS 1
+#define GLOBAL_COMMAND_PLAYERS 2
+#define GLOBAL_COMMAND_CREDITS 3
+#define	GLOBAL_COMMAND_HELP    4
+#define GLOBAL_COMMAND_PLAY    5
+#define GLOBAL_COMMAND_DEMO    6
+#define GLOBAL_COMMAND_NETWORK 7
+
+
+/** @enum eClasses
+  * @brief class definitions of everything from virtual objects up
+  *
+  * The ordering here determines the order of the drawing.
+**/
+enum eClasses
+{
+	CLASS_MISSILE = 0,
+	CLASS_BEAM,
+	CLASS_TANK,
+	CLASS_TELEPORT,
+	CLASS_DECOR_DIRT,
+	CLASS_DECOR_SMOKE,
+	CLASS_EXPLOSION,
+	CLASS_FLOATTEXT,
+	CLASS_COUNT
+};
+
+
+#ifndef HAS_TANK
+class TANK; // forwarding if not known
+#endif // HAS_TANK
+
+/// === Global functions used in several compilation units ====
+void   drawMenuBackground(eBackgroundTypes backType, int32_t tOffset,
+                          int32_t numItems);
+double interpolate       (double x1, double x2, double i);
+double Noise             (int x);
+double Noise2D           (int x, int y);
+double perlin1DPoint     (double amplitude, double scale, double xo,
+                          double lambda, int octaves);
+double perlin2DPoint     (double amplitude, double scale, double xo, double yo,
+                          double lambda, int octaves);
+void   quickChange       (bool clearerror);
 
 #include "externs.h"
 
diff --git a/src/menu.cpp b/src/menu.cpp
new file mode 100644
index 0000000..793f537
--- /dev/null
+++ b/src/menu.cpp
@@ -0,0 +1,1214 @@
+#include "optioncontent.h"
+#include "optionitemcolour.h"
+#include "main.h"
+#include "button.h"
+#include "menu.h"
+#include "player.h"
+#include "externs.h"
+#include "clock.h"
+
+#include <cassert>
+#include <exception>
+
+
+void flush_inputs(); // From files.h
+void init_mouse_cursor(); // from atanks.cpp to change the mouse cursor
+
+static int32_t MOUSE_RELEASE_DELAY = 20; // Slows down constant mouse presses
+static int32_t MOUSE_DELAY_REDUCT  = 5;  // Every so many rounds the delay is reduced
+
+
+/* -------------------------------------------
+ * --- Public constructors and destructors ---
+ * -------------------------------------------
+ */
+
+Menu::Menu(eMenuClass class_, int32_t menuX, int32_t menuY) :
+	menu_class(class_),
+	menu_x(menuX),
+	menu_y(menuY)
+{
+	// Save here to detect language changes.
+	menu_lang = env.language;
+
+	assert ( (menu_class < MC_MENUCLASS_COUNT)
+	      && "ERROR: class_ must be smaller than MC_MENUCLASS_COUNT");
+
+	// Set title according to class and language:
+	title     = MenuTitleText[menu_class][menu_lang][0];
+	title_len = text_length(font, title);
+	title_x   = menu_x + text_length(font, "W") + 2;
+
+	// Set background style
+	bgType   = env.dynamicMenuBg
+	         ? static_cast<eBackgroundTypes>(rand() % BACKGROUND_COUNT)
+	         : BACKGROUND_BLANK;
+	bgOffset = (RAND_MAX / 4) + (rand() % (RAND_MAX / 4));
+	bgItems  = (rand() % 100) + 20;
+}
+
+
+Menu::~Menu()
+{
+	// Remove all options
+	while (entry_cnt > 0) {
+		OptionItemBase* curr = tail;
+		tail = curr->getPrev();
+		delete curr; // Removes it automatically
+		--entry_cnt;
+	}
+
+	// Delete title if it was set manually
+	if (title_set && title)
+		free (const_cast<char*>(title));
+}
+
+
+
+/* ----------------------
+ * --- Public methods ---
+ * ----------------------
+ */
+
+
+/** @brief add a button to the menu
+  *
+  * This button shows a bitmap with text on it and returns a key code when
+  * clicked on.
+  *
+  * If no bitmaps are defined, a light button is drawn "by hand".
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in] title_idx index of the title text if it is listed in MenuTitleText.
+  * @param[in] title_ Pointer to a fixed title to be used instead of an indexed one.
+  * @param[in] key_code The key code to return if the button is clicked.
+  * @param[in] bmp Bitmap to use for regular display.
+  * @param[in] hover Bitmap to use when the mouse pointer hovers over the button.
+  * @param[in] released Bitmap to use when the button was clicked on.
+  * @param[in] text_only If set to true the button is only its title text.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title, display and wheel buttons.
+  * @return Number of options in the menu after adding the button.
+**/
+int32_t Menu::addButton(int32_t title_idx, const char* title_, int key_code,
+                        BITMAP* bmp, BITMAP* hover, BITMAP* released,
+                        bool text_only, int left, int top, int width, int height,
+                        int padding)
+{
+	OptionItemBase* curr        = nullptr;
+	BUTTON*         btn         = nullptr;
+	bool            title_valid = is_title_idx_valid(title_idx);
+
+	// 1) Create the button
+	try {
+		if (bmp || hover || released)
+			btn = new BUTTON(title_ ? title_
+			                 : title_valid
+			                   ? MenuTitleText[menu_class][menu_lang][title_idx]
+			                   : nullptr,
+			                 text_only, menu_x + left, menu_y + top,
+			                 bmp, hover, released);
+		else
+			btn = new BUTTON(title_ ? title_
+			                 : title_valid
+			                   ? MenuTitleText[menu_class][menu_lang][title_idx]
+			                   : nullptr,
+			                 text_only, menu_x + left, menu_y + top, width, height);
+	} catch (std::bad_alloc &e) {
+		cerr << __FUNCTION__ << " : failed to allocate new BUTTON\n";
+		cerr << " [" << e.what() << "]" << endl;
+	}
+
+	// 2) Create the option
+	try {
+		curr = new OptionItem<int, int>(key_code, nullptr,
+		                                nullptr,
+		                                title_ ? title_
+		                                : title_valid
+		                                  ? MenuTitleText[menu_class][menu_lang][title_idx]
+		                                  : "",
+		                                title_idx, btn,
+		                                left, top, width, height, padding);
+	} catch (std::bad_alloc &e) {
+		cerr << __FUNCTION__ << " : failed to allocate new ET_BUTTON option\n";
+		cerr << " [" << e.what() << "]" << endl;
+	}
+
+	// 3) Insert option:
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds a color option to pick a color value
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in] target Pointer to the target to handle.
+  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] show_size Border length of the square displaying the currently picked color.
+  * @param[in] padding Distance between title and display.
+**/
+int32_t Menu::addColor(int32_t* target, int32_t title_idx,
+                       int32_t left, int32_t top, int32_t width, int32_t height,
+                       int32_t show_size, int32_t padding)
+{
+	OptionItemBase* curr = nullptr;
+	bool title_valid = is_title_idx_valid(title_idx);
+
+	assert (title_valid && "ERROR: The given title index is invalid");
+
+	if (target && title_valid) {
+		try {
+			curr = new OptionItemColour(
+			                    target,
+			                    MenuTitleText[menu_class][menu_lang][title_idx],
+			                    title_idx,
+			                    menu_y + top, menu_x + left, width, height,
+			                    padding, show_size);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new ET_COLOR OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds a sub menu option with direct menu access
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in] menu Pointer to the menu to handle.
+  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+  * @param[in] color Regular display color of the title.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title and display.
+  * @return Number of options in the menu after adding the button.
+**/
+int32_t Menu::addMenu(Menu* menu, int32_t title_idx, int32_t color,
+                      int32_t left, int32_t top, int32_t width, int32_t height,
+                      int32_t padding)
+{
+	OptionItemBase* curr = nullptr;
+	bool title_valid = is_title_idx_valid(title_idx);
+
+	assert (title_valid && "ERROR: The given title index is invalid");
+
+	if (menu && title_valid) {
+		try {
+			curr = new OptionItemMenu(menu,
+			                    MenuTitleText[menu_class][menu_lang][title_idx],
+			                    title_idx, color,
+			                    menu_y + top, menu_x + left, width, height,
+			                    padding);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new ET_MENU OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds a sub menu option that handles a player (edit/create)
+  *
+  * Important: This _MUST_ have an action function that does the real work. This
+  * method ads an OptionItemPlayer instance, which is only a bridge to the
+  * action function.
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in,out] player_ Pointer to the PLAYER instance to handle.
+  * @param[in,out] action_ Pointer to the action function handling the button click.
+  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title and display.
+  * @return Number of options in the menu after adding the button.
+**/
+int32_t Menu::addMenu(PLAYER** player,
+                      int32_t (*action_)(PLAYER** player_, int32_t),
+                      int32_t title_idx,
+                      int32_t left, int32_t top, int32_t width, int32_t height,
+                      int32_t padding)
+{
+	OptionItemBase* curr = nullptr;
+	bool title_valid = is_title_idx_valid(title_idx);
+
+	assert (action_ && "ERROR: No action function, no player menu.");
+
+	if (player && action_) {
+		try {
+			curr = new OptionItemPlayer(
+			                player, action_,
+			                title_valid
+			                  ? MenuTitleText[menu_class][menu_lang][title_idx]
+			                  : nullptr, // The ctor uses the player name if nullptr.
+			                title_idx,
+			                menu_y + top, menu_x + left, width, height,
+			                padding);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new ET_MENU OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds a text option with editable text
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * Please also note that a title is displayed to the left of the display
+  * area.
+  *
+  * @param[in] target Pointer to the target to handle.
+  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+  * @param[in] max_len Maximum number of characters to allow.
+  * @param[in] color Regular display color of the title/target.
+  * @param[in] format The format to represent the text, used by snprintf().
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title and display.
+**/
+int32_t Menu::addText(char* target, int32_t title_idx, uint32_t max_len,
+                      int32_t color, const char* format,
+                      int left, int top, int width, int height, int padding)
+{
+	OptionItemBase* curr = nullptr;
+	bool title_valid = is_title_idx_valid(title_idx);
+
+	assert (title_valid && "ERROR: The given title index is invalid");
+
+	if (target && title_valid) {
+		try {
+			curr = new OptionItem<char, uint32_t>(
+			                    target, max_len, color, ET_TEXT,
+			                    MenuTitleText[menu_class][menu_lang][title_idx],
+			                    title_idx, format,
+			                    menu_y + top, menu_x + left, width, height,
+			                    padding);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new TEXT OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds an TC_TOGGLE feeding a boolean using a variable title
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in] target Pointer to the target to handle.
+  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+  * @param[in] color Regular display color of the title/target.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title and display.
+**/
+int32_t Menu::addToggle(bool* target, int32_t title_idx, int32_t color,
+                        int left, int top, int width, int height, int padding)
+{
+	OptionItemBase* curr = nullptr;
+	bool title_valid = is_title_idx_valid(title_idx);
+
+	assert (title_valid && "ERROR: The given title index is invalid");
+
+	if (target && title_valid) {
+		try {
+			curr = new OptionItem<bool, uint32_t>(
+			                    target, nullptr, ET_TOGGLE,
+			                    MenuTitleText[menu_class][menu_lang][title_idx],
+			                    title_idx, nullptr, color, TC_NONE,
+			                    0, 0, 0, nullptr,
+			                    menu_y + top, menu_x + left, width, height,
+			                    padding, nullptr);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new TOGGLE OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds an TC_TOGGLE feeding a boolean using a fixed title
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in] target Pointer to the target to handle.
+  * @param[in] title Fixed title to display.
+  * @param[in] color Regular display color of the title/target.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title and display.
+**/
+int32_t Menu::addToggle(bool* target, const char* title_, int32_t color,
+                        int left, int top, int width, int height, int padding)
+{
+	OptionItemBase* curr = nullptr;
+
+	assert (title_ && "ERROR: title_ must be set but is nullptr");
+
+	if (target && title_) {
+		try {
+			curr = new OptionItem<bool, uint32_t>(
+			                    target, nullptr, ET_TOGGLE,
+			                    title_, -1, nullptr, color, TC_NONE,
+			                    0, 0, 0, nullptr,
+			                    menu_y + top, menu_x + left, width, height,
+			                    padding, nullptr);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new TOGGLE OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/** @brief This adds an TC_TOGGLE handling PLAYER::selected
+  *
+  * Please note: The position @a left / @a top are relative to
+  * the menu position.
+  *
+  * @param[in] player Pointer pointer to the player to handle.
+  * @param[in] left Relative left position of the display area to the menu.
+  * @param[in] top Relative top position of the display area to the menu.
+  * @param[in] width Width of the display area. The real width might be larger.
+  * @param[in] height Height of the display area.
+  * @param[in] padding Distance between title and display.
+**/
+int32_t Menu::addToggle(PLAYER** player,
+                        int left, int top, int width, int height, int padding)
+{
+	OptionItemBase* curr = nullptr;
+
+	assert (player && *player
+			&& "ERROR: For a player toggle *player must be valid.");
+
+	if (player && *player) {
+		try {
+			curr = new OptionItemPlayer(player, nullptr,
+			                            nullptr, -1,
+			                            menu_y + top, menu_x + left,
+			                            width, height, padding);
+		} catch (std::bad_alloc &e) {
+			cerr << __FUNCTION__ << " : failed to allocate new ET_TOGGLE OptionItem\n";
+			cerr << " [" << e.what() << "]" << endl;
+		}
+	}
+
+	return this->insert_option(curr);
+}
+
+
+/// @brief call clear_display(full_display) on all entries
+void Menu::clearAll(bool full_clear)
+{
+	OptionItemBase* curr = root;
+	while (curr) {
+		curr->clear_display(full_clear);
+		curr = curr->getNext();
+	}
+}
+
+
+/// @brief return number of menu elements
+int32_t Menu::count()
+{
+	return entry_cnt;
+}
+
+
+/// @brief deletes entry with index @a index
+int32_t Menu::delete_entry(int32_t index)
+{
+	if ( (index >= 0) && (index < entry_cnt) ) {
+		OptionItemBase* curr = this->operator[](index);
+		if (curr) {
+			if (root == curr)
+				root = curr->getNext();
+			if (tail == curr)
+				tail = curr->getPrev();
+			delete curr; // This removes it from the list.
+			--entry_cnt;
+		}
+	}
+
+	return entry_cnt;
+}
+
+
+/// @brief call display(full_display) on all entries
+void Menu::displayAll(bool full_display)
+{
+	OptionItemBase* curr = root;
+	while (curr) {
+		// If a text field (ET_TEXT) is selected, it must be forced
+		// to redraw, so the cursor flipping can be in effect:
+		if (curr->is_selected() && (ET_TEXT == curr->getType()))
+			curr->cursor_flip();
+
+		curr->display(full_display);
+		curr = curr->getNext();
+	}
+}
+
+
+/** @brief distribute a range of items over specified space
+  *
+  * This method distributes the items with the index @a first_idx to
+  * @a last_idx over columns and rows according to the largest item
+  * and available space.
+  *
+  * @param[in] first_idx The first index to distribute
+  * @param[in] last_idx The last index to distribute
+  * @param[in] list_width The total width to distribute over if needed
+  * @param[in] list_height The total height to distribute over
+  * @param[in] y_off Y-Offset where the list starts
+  * @param[in] do_update whether to clear the old display or not.
+  *
+**/
+void Menu::distribute(int32_t first_idx, int32_t last_idx,
+                      int32_t list_width, int32_t list_height,
+                      int32_t y_off, bool do_update)
+{
+	int32_t item_count   = last_idx - first_idx + 1;
+	int32_t item_height  = 0;
+	int32_t item_width   = 0;
+
+	// valid?
+	assert( ( last_idx >= first_idx)
+		  && "ERROR: last_idx must not be smaller than first_idx!");
+	assert( ( last_idx < entry_cnt )
+		  && "ERROR: last_idx is out of range");
+	if ( (last_idx < first_idx) || (last_idx >= entry_cnt) )
+		return;
+
+	// 1: Determine minimum width and height:
+	int32_t curr_w = 0, curr_h = 0;
+
+	for (int32_t num = first_idx; num <= last_idx; ++num) {
+        OptionItemBase* curr = this->operator[](num);
+        if (curr) {
+			curr->getDimension(curr_w, curr_h);
+			if (curr_w > item_width)
+				item_width = curr_w;
+			if (curr_h > item_height)
+				item_height = curr_h;
+        }
+	}
+
+	// The width must be increased, as items might get selected:
+	item_width += select_text_len;
+
+	// Set base values
+	int32_t rows     = list_height / item_height;
+	int32_t cols     = (item_count / rows)
+	                 + (item_count % rows ? 1 : 0);
+	int32_t colOff   = (list_width / 2) - (cols * (item_width / 2));
+
+	for (int32_t idx = first_idx; idx <= last_idx; ++idx) {
+        OptionItemBase* curr = this->operator[](idx);
+        if (curr) {
+			int32_t num     = idx - first_idx;
+			int32_t cur_col = num / rows;
+			int32_t x       = colOff + (cur_col * item_width);
+			int32_t y       = item_height * (num % rows);
+			curr->move(menu_x + x, menu_y + y_off + y, do_update);
+        }
+	}
+}
+
+
+/// @brief return pointer to the currently selected entry or nullptr if none is selected
+OptionItemBase* Menu::getSelected()
+{
+	if ( (entry_sel > -1) && (entry_sel < entry_cnt))
+		return this->operator[](entry_sel);
+	return nullptr;
+}
+
+
+/// @brief return a const pointer to the menu title
+const char* Menu::getTitle() const
+{
+	return title;
+}
+
+
+/// @brief move an entry somewhere else
+void Menu::move_entry(int32_t from_idx, int32_t to_idx)
+{
+	assert( (from_idx > -1) && (from_idx < entry_cnt)
+	     && "ERROR: from_idx is out of range!");
+	assert( (to_idx > -1) && (to_idx < entry_cnt)
+	     && "ERROR: to_idx is out of range!");
+
+	if (from_idx != to_idx) {
+		OptionItemBase* toMove = operator[](from_idx);
+		assert (toMove && "ERROR: Something is completely FUBAR here!");
+
+		if (to_idx > from_idx)
+			// in this case the spot will move one down
+			--to_idx;
+
+		// Take it out:
+		if (0 == from_idx)
+			// It is root
+			root = toMove->getNext();
+		if ((entry_cnt - 1) == from_idx)
+			// Or tail
+			tail = toMove->getPrev();
+		toMove->remove();
+		--entry_cnt;
+
+		// And re-insert
+		if (0 == to_idx) {
+			toMove->insert_before(root);
+			root = toMove;
+		} else if (entry_cnt == to_idx) {
+			toMove->insert_after(tail);
+			tail = toMove;
+		} else
+			toMove->insert_before(this->operator[](to_idx));
+		++entry_cnt;
+	} // end of to_idx != from_idx
+}
+
+
+/// @brief do a redraw of one element
+void Menu::redraw(int32_t index, bool update_full)
+{
+	if ( (index >= 0) && (index < entry_cnt) ) {
+		OptionItemBase* curr = this->operator[](index);
+		if (curr) {
+			curr->clear_display(update_full);
+			curr->display(update_full);
+		}
+	}
+}
+
+
+/// @brief do a full redraw of everything
+void Menu::redrawAll(bool full_redraw)
+{
+	SHOW_MOUSE(nullptr)
+	this->clearAll(full_redraw);
+
+	// If this is a full redraw, the background and
+	// menu title must be drawn as well.
+	if (full_redraw) {
+		if (++bgOffset == INT_MAX)
+			bgOffset = 0;
+		drawMenuBackground (bgType, bgOffset, bgItems);
+		textout_ex (global.canvas, font, title, title_x + 2, menu_y + 12, BLACK, -1);
+		textout_ex (global.canvas, font, title, title_x + 5, menu_y + 14, WHITE, -1);
+	}
+
+	this->displayAll(full_redraw);
+	SHOW_MOUSE(global.canvas)
+
+	if (full_redraw)
+		quickChange(false);
+
+}
+
+
+void Menu::setLanguage(bool autorefresh)
+{
+	if (env.language != menu_lang) {
+		menu_lang = env.language;
+
+		if (!title_set)
+			title = MenuTitleText[menu_class][menu_lang][0];
+
+		OptionItemBase*    curr   = root;
+		const char* const* titles = MenuTitleText[menu_class][menu_lang];
+
+		while (curr) {
+			int32_t title_idx = curr->getTitleIdx();
+
+			// 1: Set new title (if not manually set)
+			if (title_idx > -1)
+				curr->setTitle(titles[title_idx]);
+
+			// 2: Set new text array if based on a pre-set
+			if (curr->needs_text()) {
+				const char* const* texts = OptionClassText[curr->getTextClass()][menu_lang];
+				curr->setTexts(const_cast<const char**>(texts));
+			}
+
+			// 3: If this is a sub-menu, call an update dispatcher
+			if (ET_MENU == curr->getType())
+				static_cast<OptionItemMenu*>(curr)->setLanguage();
+
+			curr = curr->getNext();
+		}
+
+		if (autorefresh)
+			this->redrawAll(true);
+	}
+}
+
+
+void Menu::setTitle(const char* new_title, bool autorefresh)
+{
+	if (new_title) {
+		// Delete old title if it was set already
+		if (title_set && title)
+			free(const_cast<char*>(title));
+
+		title = strdup(new_title);
+		title_set = true;
+
+		if (autorefresh)
+			this->redrawAll(true);
+	}
+}
+
+
+/* ------------------------
+ * --- Public operators ---
+ * ------------------------
+ */
+
+
+/** @brief Parentheses operator to use an instance like a function
+  *
+  * The operator hands over input control to the menu. It will return only
+  * if an option is a button that returns a key code. So make sure to have
+  * at least one button per menu that lets you get out ... or stay forever. ;)
+  *
+  * <I>Note</I>: As a safety measure the operator checks for the existence of
+  * a returning button and asserts that existence. New menus should be checked
+  * in debug mode at least once.
+  *
+  * @return The key code of a clicked exiting button.
+**/
+int32_t Menu::operator()()
+{
+	bool has_exit_button = false;
+	OptionItemBase* curr = root;
+
+	while (!has_exit_button && curr) {
+		has_exit_button = curr->isExitButton();
+		curr = curr->getNext();
+	}
+
+	assert(has_exit_button && "ERROR: A Menu without an exit button is unleavable!");
+
+	// Needed Loop values :
+	int32_t key_code        = -1;
+	int32_t end_event       = 0;
+	int32_t allegro_key     = 0;
+	bool    mlb_is_pressed  = false; // Left mouse button is pressed,
+	bool    mlb_is_released = true;  // and was released.
+	bool    mrb_is_pressed  = false; // Right mouse button is pressed,
+	bool    mrb_is_released = true;  // and was released.
+	int32_t ms_per_frame    = 1000 / env.frames_per_second;
+	int32_t mlb_x           = 0;
+	int32_t mlb_y           = 0;
+	int32_t mouse_clock     = MOUSE_RELEASE_DELAY;
+	int32_t mouse_round     = 0;
+	int32_t mouse_reduct    = 0;
+	bool    has_ctrl_down   = false;
+	eEntryType last_clicked = ET_NONE;
+
+	flush_inputs();
+	WIN_CLOCK_INIT
+	menu_ms_reset();
+
+	// Set background style
+	bgType   = env.dynamicMenuBg
+	         ? static_cast<eBackgroundTypes>(rand() % BACKGROUND_COUNT)
+	         : BACKGROUND_BLANK;
+	bgOffset = (RAND_MAX / 4) + (rand() % (RAND_MAX / 4));
+	bgItems  = (rand() % 100) + 20;
+
+	// Initial display:
+	redrawAll(true);
+
+	/* ---------------------------------------
+	 * --- Input handling and drawing loop ---
+	 * ---------------------------------------
+	 */
+	while (-1 == key_code) {
+		int32_t ms_unused = ms_per_frame - menu_ms_get();
+		if (ms_unused > 0)
+			MSLEEP(ms_unused)
+		redrawAll(true);
+
+		if (global.isCloseBtnPressed()) {
+			key_code  = KEY_ESC;  // Exit loop
+			end_event = key_code; // Exit menu
+			continue;
+		}
+
+		/// --------------------------------------
+		/// --- A) Pre-handle key press events ---
+		/// --------------------------------------
+		has_ctrl_down = (key[KEY_LCONTROL] || key[KEY_RCONTROL]);
+
+		if ( keypressed() ) {
+			allegro_key = readkey();
+			key_code = allegro_key >> 8;
+			if (KEY_DOWN == key_code) {
+				this->selectNext();
+				key_code = -1;
+			} else if (KEY_UP == key_code) {
+				this->selectPrev();
+				key_code = -1;
+			} else if (KEY_ENTER_PAD == key_code)
+				key_code = KEY_ENTER;
+
+		} // End of having a pressed key
+
+
+		/// --------------------------------------
+		/// --- B) Handle mouse button events  ---
+		/// --------------------------------------
+
+		mlb_x = mouse_x;
+		mlb_y = mouse_y;
+
+		// Set mouse button status anew
+		mlb_is_pressed = mouse_b & 1 ? true : false;
+		mrb_is_pressed = mouse_b & 2 ? true : false;
+
+		// Fix release status on mouse button states:
+		if (!mlb_is_pressed) mlb_is_released = true;
+		if (!mrb_is_pressed) mrb_is_released = true;
+
+		// reset mouse clock if both are released
+		if (mlb_is_released && mrb_is_released) {
+			mouse_clock  = MOUSE_RELEASE_DELAY;
+			mouse_round  = 0;
+			mouse_reduct = 0;
+		}
+
+		// Be sure only new left mouse button presses are recorded
+		if (mlb_is_released && mlb_is_pressed)
+			mlb_is_released = false;
+		else if (mlb_is_pressed) {
+			if ( (--mouse_clock > 0)
+			  || ( (ET_VALUE != last_clicked)
+				&& (ET_COLOR != last_clicked) ) )
+				mlb_is_pressed = false;
+		}
+
+		// The same applies to the right button
+		if (mrb_is_released && mrb_is_pressed && !mlb_is_pressed)
+			mrb_is_released = false;
+			// Note: But the release is set to false anyway, so pressing
+			// both buttons will not result in a right mouse button event
+			// if held and the left button is released.
+		else if (mrb_is_pressed) {
+			if ( (--mouse_clock > 0)
+			  || ( (ET_VALUE != last_clicked)
+				&& (ET_COLOR != last_clicked) ) )
+				mrb_is_pressed = false;
+		}
+
+		// Handle the mouse delay:
+		if (!mouse_clock) {
+			if ( (ET_VALUE == last_clicked)
+			  || (ET_COLOR == last_clicked) ) {
+				if (MOUSE_DELAY_REDUCT == ++mouse_round) {
+					mouse_round = 0;
+					if ( (ET_COLOR == last_clicked)
+					  || (++mouse_reduct >= MOUSE_RELEASE_DELAY) )
+						mouse_reduct = MOUSE_RELEASE_DELAY - 1;
+				}
+			} else
+				mouse_reduct = 0;
+			mouse_clock = MOUSE_RELEASE_DELAY - mouse_reduct;
+		}
+
+		// Determine whether a click hit something
+		int32_t event = mlb_is_pressed || mrb_is_pressed
+		              ? selectClicked(mlb_x, mlb_y)
+		              : 0;
+
+
+		/// --------------------------------
+		/// --- C) Handle current events ---
+		/// --------------------------------
+
+		if ( event || (key_code > 0) ) {
+			curr = getSelected();
+			if (curr) {
+				eEntryType type      = curr->getType();
+				bool       old_mouse = env.osMouse; // To catch mouse changes
+
+				// Note whether clicked on elements for the clock delay reduction
+				if (event)
+					last_clicked = type;
+				else
+					last_clicked = ET_NONE;
+
+				// ET_VALUE needs handling for left/right keys:
+				if (ET_VALUE == type) {
+					if (KEY_RIGHT == key_code)
+						event = 1;
+					else if (KEY_LEFT == key_code)
+						event = -1;
+					// If the right mouse button or ctrl key was pressed,
+					// multiply the event by 10
+					if (mrb_is_pressed || has_ctrl_down)
+						event *= 10;
+				}
+
+				// Set key_code to activation result
+				key_code = curr->activate(event, mlb_x, mlb_y, allegro_key);
+
+				// If this was a sub menu, redraw the current menu:
+				if (ET_MENU == type)
+					redrawAll(true);
+
+				// Some elements trigger end-events
+				if ( (key_code > 0)
+					// If this was a sub menu with KEY_ESC result,
+					// it just means that the sub menu was closes:
+				  && ( (ET_MENU != type)
+				    || (KEY_ESC != key_code) ) ) {
+					end_event = key_code;
+				} else
+					key_code = -1;
+				allegro_key = 0;
+
+				// If the mouse was changed, the change must be performed
+				// at once. If we didn't do this here, switching back to
+				// standard mouse makes it invisible until the menu exits.
+				if (old_mouse != env.osMouse)
+					init_mouse_cursor();
+
+			} // End of having a menu entry to handle
+		} // End of having mouse button or key event
+
+		// Update non-OS mouse movements
+		SHOW_MOUSE(global.canvas)
+
+	} // End of input/drawing loop
+
+	WIN_CLOCK_REMOVE
+
+	// As the menu does not clear error messages, a possible
+	// message must be cleared now:
+	if (errorMessage)
+		errorMessage = nullptr;
+
+	return end_event;
+
+} // End of Menu::operator()()
+
+
+/** @brief Get a stored option by index
+  *
+  * If @a index is out of range, nullptr is returned.
+  *
+  * @param[in] index The index of the wanted option, starting with 0.
+**/
+OptionItemBase* Menu::operator[](int32_t index)
+{
+	OptionItemBase* result = nullptr;
+
+	if ((-1 < index) && (entry_cnt > index)) {
+		int32_t cur_idx = 0;
+		bool    go_up   = true;
+		result          = root;
+
+		// Start front or end?
+		if (index > (entry_cnt / 2)) {
+			result  = tail;
+			cur_idx = entry_cnt - 1;
+			go_up   = false;
+		}
+
+		// Just wander, this should be safe.
+		while (result && (cur_idx != index)) {
+			if (go_up) {
+				result = result->getNext();
+				++cur_idx;
+			} else {
+				result = result->getPrev();
+				--cur_idx;
+			}
+
+			assert(result && "ERROR: Something is wrong with the list!");
+		}
+	} // End of having a sane index
+
+	return result;
+}
+
+
+/* -----------------------
+ * --- Private methods ---
+ * -----------------------
+ */
+
+/// @brief simple singly list insert
+int32_t Menu::insert_option(OptionItemBase* new_opt)
+{
+	if (new_opt) {
+		// Insert into list:
+		if (tail) {
+			new_opt->insert_after(tail);
+			tail = new_opt;
+		} else {
+			root = new_opt;
+			tail = new_opt;
+		}
+
+		++entry_cnt;
+	}
+	return entry_cnt;
+}
+
+
+/// @brief simple singly list insert with title setting
+int32_t Menu::insert_option(OptionItemBase* new_opt, int32_t title_idx,
+							const char* title_)
+{
+	if (new_opt) {
+		if (title_)
+			new_opt->setTitle(title_);
+		else if (is_title_idx_valid(title_idx))
+			new_opt->setTitle(MenuTitleText[menu_class][menu_lang][title_idx]);
+		return insert_option(new_opt);
+	}
+	return entry_cnt;
+}
+
+
+/// @brief return true if @a title_idx is lower than the first 0x0 entry
+bool Menu::is_title_idx_valid(int32_t title_idx)
+{
+	int32_t curr_idx = 0;
+	const char* const* titles = MenuTitleText[menu_class][menu_lang];
+
+	while ( (curr_idx < title_idx) && titles[curr_idx] )
+		++curr_idx;
+
+	return ( (title_idx > -1) && (curr_idx == title_idx) && titles[curr_idx]);
+}
+
+
+/** @brief This method selects the entry under the mouse.
+  *
+  * If the mouse position, described by the @a x and @a y parameters,
+  * is not over any entry, nothing happens and 0 is returned.
+  *
+  * If the activated entry is an ET_VALUE and one of the change buttons is hit,
+  * the method returns -1 for down and +1 for up.
+  *
+  * If the activated entry is an ET_BUTTON with associated key code, the key
+  * code is returned.
+  *
+  * In all other cases 0 is returned, as clicking must be activated manually.
+  *
+  * @param[in] x Mouse x coordinate.
+  * @param[in] y Mouse y coordinate.
+  * @return -1/+1 for ET_VALUE change buttons, associated key code for ET_BUTTON
+  * and 0 in all other cases.
+**/
+int32_t Menu::selectClicked(int32_t x, int32_t y)
+{
+	OptionItemBase* curr     = root;
+	OptionItemBase* result   = nullptr;
+	int32_t         retval   = 0;
+	int32_t         curr_idx = -1;
+
+	while (curr && !result) {
+		++curr_idx;
+		if (curr->is_click_in(x, y, retval))
+			result = curr;
+		else
+			curr = curr->getNext();
+	}
+
+	if (result && !result->is_selected()) {
+		unselect();
+		entry_sel = curr_idx;
+		result->select();
+	}
+
+	return retval;
+}
+
+
+/** @brief unselect current selected entry (if any) and select the next.
+  * If no entry is selected, the first one will be chosen.
+  * If the last entry is selected, no entry will be chosen.
+**/
+void Menu::selectNext()
+{
+	OptionItemBase* curr = nullptr;
+
+	if (entry_sel > -1) {
+		curr = operator[](entry_sel);
+		if (curr)
+			curr->unselect();
+	}
+
+	// If this was the last entry, none is to be selected
+	if (++entry_sel >= entry_cnt)
+		entry_sel = -1;
+	else {
+		if (curr)
+			// The previous was unselected
+			curr = curr->getNext();
+		else
+			curr = operator[](entry_sel);
+
+		// Be sure the container works properly!
+		assert(curr && "ERROR: Something is wrong with the OptionEntry list!");
+
+		if (curr)
+			curr->select();
+	}
+}
+
+
+/** @brief unselect current selected entry (if any) and select the previous.
+  * If no entry is selected, the first one will be chosen.
+  * If the last entry is selected, no entry will be chosen.
+**/
+void Menu::selectPrev()
+{
+	OptionItemBase* curr = nullptr;
+
+	if (entry_sel > -1) {
+		curr = operator[](entry_sel);
+		if (curr)
+			curr->unselect();
+	}
+
+	// If this was the first entry, none is to be selected
+	if (--entry_sel != -1) {
+		// Rotate to the end if none was selected
+		if (entry_sel < -1)
+			entry_sel = entry_cnt - 1;
+
+		if (curr)
+			// The next was unselected
+			curr = curr->getPrev();
+		else
+			curr = operator[](entry_sel);
+
+		// Be sure the container works properly!
+		assert(curr && "ERROR: Something is wrong with the OptionEntry list!");
+
+		if (curr)
+			curr->select();
+	}
+}
+
+
+/// @brief little helper to be able to add options from inside the header
+void Menu::setTexts(OptionItemBase* item,
+                    const char** texts,
+                    eTextClass text_class)
+{
+	assert(  item && (texts || (TC_FREETEXT != text_class))
+	      && (TC_NONE != text_class) && "ERROR: This does not fit at all!");
+    if (item) {
+		if ( (TC_FREETEXT == text_class) && texts) {
+			item->setTextClass(text_class);
+			item->setTexts(texts);
+		} else if (TC_NONE != TC_FREETEXT) {
+			item->setTextClass(text_class);
+			item->setTexts(const_cast<const char**>(OptionClassText[text_class][menu_lang]));
+		}
+    }
+}
+
+
+/** @brief unselect current selected entry (if any).
+  * If no entry is selected, nothing happens.
+**/
+void Menu::unselect()
+{
+	OptionItemBase* curr = nullptr;
+
+	if (entry_sel > -1) {
+		curr = operator[](entry_sel);
+		if (curr)
+			curr->unselect();
+		entry_sel = -1;
+	}
+}
+
+
+/// @brief display function for tank bitmaps plus tank type text
+bool display_tank_desc(int32_t* tanknum, int32_t x, int32_t y)
+{
+	assert(tanknum && "ERROR: tanknum must be set");
+
+	assert(*tanknum > -1 && "ERROR: tanknum too low");
+	assert(*tanknum < TT_TANK_COUNT && "ERROR: tanknum too high");
+
+	if (!tanknum || (*tanknum < 0) || (*tanknum >= TT_TANK_COUNT))
+		return false;
+
+	BITMAP*     tank_bmp   = env.tank[   *tanknum ? *tanknum + TO_TANK   : *tanknum];
+	BITMAP*     turr_bmp   = env.tankgun[*tanknum ? *tanknum + TO_TURRET : *tanknum];
+	int32_t     tank_off_x = ROUNDu(tank_bmp->w / 2);
+	int32_t     tank_off_y =        tank_bmp->h;
+	int32_t     turr_off_x = ROUNDu(turr_bmp->w / 2);
+	int32_t     turr_off_y = ROUNDu(turr_bmp->h / 2) - 2;
+	int32_t     tank_x     = x + tank_off_x + 1;
+	int32_t     tank_y     = y + turr_off_y + 1;
+	int32_t     text_y     = tank_y + (tank_off_y / 2) - (env.fontHeight / 2);
+	int32_t     text_x     = tank_x + tank_off_x + 5;
+	const char* tank_text  = OptionClassText[TC_TANKTYPE][env.language][*tanknum];
+
+	draw_sprite   (global.canvas, tank_bmp, tank_x - tank_off_x, tank_y);
+	rotate_sprite (global.canvas, turr_bmp, tank_x - turr_off_x, tank_y - turr_off_y,
+	               itofix(224) );
+
+	textout_ex (global.canvas, font, tank_text, text_x, text_y, BLACK, -1);
+
+	global.make_update(x, y, text_x + text_length(font, tank_text) - x,
+	                   tank_y + turr_off_y + tank_off_y - y);
+
+	return true;
+}
+
diff --git a/src/menu.h b/src/menu.h
index 2f5bf24..7a933e3 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -1,9 +1,9 @@
-#ifndef	MENU_HEADER
-#define	MENU_HEADER
+#pragma once
+#ifndef ATANKS_SRC_MENU_H_INCLUDED
+#define ATANKS_SRC_MENU_H_INCLUDED
 
 /*
  * atanks - obliterate each other with oversize weapons
- * Copyright (C) 2003  Thomas Hudson
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,52 +18,425 @@
  * 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.
- * */
-#include "globaldata.h"
+ *
+ */
+
+#include "optionitem.h"
+#include "optionitemmenu.h"
+#include "optionitemplayer.h"
+#include "button.h"
+#include <new> // for bad_alloc exception
 
 
-#define PLAY_GAME 1
-#define LOAD_GAME 2
-#define ESC_MENU 3
+/** @file menu.h
+  * @brief Declare Menu class for self managing menus
+**/
 
-#define TEXT_BOX_LENGTH 14
+
+/** @enum eMenuReturnCodes
+  * @brief Standard return codes for the main loop
+**/
+enum eMenuReturnCodes {
+	MRC_None = 0,
+	MRC_Play_Game,
+	MRC_Load_Game,
+	MRC_Esc_Menu
+};
 
 
-enum	menuEntryType
+/** @class Menu
+  * @brief A class to build menus out of option items.
+  *
+  * @todo : Write more
+**/
+class Menu
 {
-  OPTION_MENUTYPE, OPTION_DOUBLETYPE, OPTION_TOGGLETYPE, OPTION_SPECIALTYPE, OPTION_ACTIONTYPE, OPTION_TEXTTYPE, OPTION_COLORTYPE
+public:
+
+	/* -------------------------------------------
+	 * --- Public constructors and destructors ---
+	 * -------------------------------------------
+	 */
+
+	explicit Menu(eMenuClass class_, int32_t menuX, int32_t menuY);
+	~Menu();
+
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	// Add a button without action function.
+	int32_t addButton(int32_t title_idx, const char* title_, int32_t key_code,
+	                  BITMAP* bmp, BITMAP* hover,
+	                  BITMAP* released, bool text_only,
+	                  int32_t left, int32_t top, int32_t width, int32_t height,
+	                  int32_t padding);
+
+
+	// Add a color option
+	int32_t addColor(int32_t* target, int32_t title_idx,
+	                 int32_t left, int32_t top, int32_t width, int32_t height,
+	                 int32_t show_size, int32_t padding);
+
+
+	// Add a sub menu option with Menu target
+	int32_t addMenu(Menu* menu, int32_t title_idx, int32_t color,
+	                int32_t left, int32_t top, int32_t width, int32_t height,
+	                int32_t padding);
+
+
+	// Add a sub menu option with PLAYER target (set title_idx to -1 to use player name)
+	int32_t addMenu(PLAYER** player,
+	                int32_t (*action_)(PLAYER** player_, int32_t),
+	                int32_t title_idx,
+	                int32_t left, int32_t top, int32_t width, int32_t height,
+	                int32_t padding);
+
+
+	// Special minimum variant for editable text options
+	int32_t addText(char* target, int32_t title_idx, uint32_t max_len,
+	                int32_t color, const char* format,
+	                int32_t left, int32_t top, int32_t width, int32_t height,
+	                int32_t padding);
+
+
+	/** @brief This adds a text option with readonly text
+	  *
+	  * Please note: The position @a left / @a top are relative to
+	  * the menu position.
+	  *
+	  * Please also note that a title is displayed to the left of the display
+	  * area.
+	  *
+	  * @param[in] target Pointer to the target to display.
+	  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+	  * @param[in] color Regular display color of the title/target.
+	  * @param[in] format The format to represent the target, used by snprintf().
+	  * @param[in] left Relative left position of the display area to the menu.
+	  * @param[in] top Relative top position of the display area to the menu.
+	  * @param[in] width Width of the display area. The real width might be larger.
+	  * @param[in] height Height of the display area.
+	  * @param[in] padding Distance between title and display.
+	**/
+	template<typename tgt_T>
+	int32_t addText(tgt_T* target, int32_t title_idx,
+	                int32_t color, const char* format,
+	                int32_t left, int32_t top, int32_t width, int32_t height,
+	                int32_t padding)
+	{
+		OptionItemBase* curr = nullptr;
+		bool title_valid = is_title_idx_valid(title_idx);
+
+		assert (title_valid && "ERROR: The given title index is invalid");
+
+		if (target && title_valid) {
+			try {
+				curr = new OptionItem<tgt_T, int32_t>(
+									target, 0, color, ET_TEXT,
+									"", title_idx, format,
+									menu_y + top, menu_x + left, width, height,
+									padding);
+			} catch (std::bad_alloc &e) {
+				cerr << __FUNCTION__ << " : failed to allocate new TEXT OptionItem\n";
+				cerr << " [" << e.what() << "]" << endl;
+			}
+		}
+
+		return this->insert_option(curr, title_idx, nullptr);
+	}
+
+
+	// Special minimum variant for toggle types feeding a bool with variable title
+	int32_t addToggle(bool* target, int32_t title_idx, int32_t color,
+	                  int32_t left, int32_t top, int32_t width, int32_t height,
+	                  int32_t padding);
+
+
+	// Special minimum variant for toggle types feeding a bool with fixed title
+	int32_t addToggle(bool* target, const char* title_, int32_t color,
+	                  int32_t left, int32_t top, int32_t width, int32_t height,
+	                  int32_t padding);
+
+
+	// Special minimum variant for toggle types handling PLAYER::selected
+	int32_t addToggle(PLAYER** player,
+	                  int32_t left, int32_t top, int32_t width, int32_t height,
+	                  int32_t padding);
+
+
+	/** @brief Simple ET_VALUE option with direct value representation
+	  *
+	  * Please note: The position @a left / @a top are relative to
+	  * the menu position.
+	  *
+	  * Please also note that a title is displayed to the left of the display
+	  * area and wheel buttons to the right.
+	  *
+	  * @param[in] target Pointer to the target to handle.
+	  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+	  * @param[in] color Regular display color of the title/target.
+	  * @param[in] minimum Minimum value for ET_VALUE targets.
+	  * @param[in] maximum Maximum value for ET_VALUE targets.
+	  * @param[in] increment Value to increment/decrement the target on activation.
+	  * @param[in] format printf format that can pretty print @a target.
+	  * @param[in] left Relative left position of the display area to the menu.
+	  * @param[in] top Relative top position of the display area to the menu.
+	  * @param[in] width Width of the display area. The real width might be larger.
+	  * @param[in] height Height of the display area.
+	  * @param[in] padding Distance between title, display and wheel buttons.
+	**/
+	template<typename tgt_T, typename opt_T = int32_t>
+	int32_t addValue(tgt_T* target, int32_t title_idx, int32_t color,
+	                 opt_T minimum, opt_T maximum, opt_T increment,
+	                 const char* format,
+	                 int32_t left, int32_t top, int32_t width, int32_t height,
+	                 int32_t padding)
+	{
+		OptionItemBase* curr = nullptr;
+
+		if (target) {
+			try {
+				curr = new OptionItem<tgt_T, opt_T>(
+								target, "", title_idx,
+								nullptr, color, TC_NONE,
+								minimum, maximum, increment, format,
+								menu_y + top, menu_x + left, width, height,
+								padding, nullptr);
+			} catch (std::bad_alloc &e) {
+				cerr << __FUNCTION__ << " : failed to allocate new OptionItem\n";
+				cerr << " [" << e.what() << "]" << endl;
+			}
+		}
+
+		return this->insert_option(curr, title_idx, nullptr);
+	}
+
+
+	/** @brief Simple option with text array representation
+	  *
+	  * Please note: The position @a left / @a top are relative to
+	  * the menu position.
+	  *
+	  * Please also note that a title is displayed to the left of the display
+	  * area.
+	  *
+	  * @param[in] target Pointer to the target to handle.
+	  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+	  * @param[in] texts Free text array.
+	  * @param[in] color Regular display color of the title/target.
+	  * @param[in] text_class The text class, set to TC_FREETEXT to use @a texts.
+	  * @param[in] maximum Maximum value for ET_VALUE targets.
+	  * @param[in] left Relative left position of the display area to the menu.
+	  * @param[in] top Relative top position of the display area to the menu.
+	  * @param[in] width Width of the display area. The real width might be larger.
+	  * @param[in] height Height of the display area.
+	  * @param[in] padding Distance between title and display.
+	**/
+	template<typename tgt_T, typename opt_T = int32_t>
+	int32_t addValue(tgt_T* target, int32_t title_idx,
+	                 const char** texts, int32_t color,
+	                 eTextClass text_class, opt_T maximum,
+	                 int32_t left, int32_t top, int32_t width, int32_t height,
+	                 int32_t padding)
+	{
+		OptionItemBase* curr = nullptr;
+
+		if (target) {
+			try {
+				curr = new OptionItem<tgt_T, opt_T>(
+								target, "", title_idx,
+								nullptr, color, TC_NONE, 0, maximum, 1, nullptr,
+								menu_y + top, menu_x + left, width, height,
+								padding, nullptr);
+				this->setTexts(curr, texts, text_class);
+			} catch (std::bad_alloc &e) {
+				cerr << __FUNCTION__ << " : failed to allocate new OptionItem\n";
+				cerr << " [" << e.what() << "]" << endl;
+			}
+		}
+
+		return this->insert_option(curr, title_idx, nullptr);
+	}
+
+
+	/** @brief Value option with text array representation and display function
+	  *
+	  * Please note: The position @a left / @a top are relative to
+	  * the menu position.
+	  *
+	  * Please also note that a title is displayed to the left of the display
+	  * area.
+	  *
+	  * @param[in] target Pointer to the target to handle.
+	  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+	  * @param[in] texts Free text array.
+	  * @param[in] color Regular display color of the title/target.
+	  * @param[in] text_class The text class, set to TC_FREETEXT to use @a texts.
+	  * @param[in] maximum Maximum value for ET_VALUE targets.
+	  * @param[in] left Relative left position of the display area to the menu.
+	  * @param[in] top Relative top position of the display area to the menu.
+	  * @param[in] width Width of the display area. The real width might be larger.
+	  * @param[in] height Height of the display area.
+	  * @param[in] padding Distance between title and display.
+	  * @param[in] display_ optional display function to use.
+	**/
+	template<typename tgt_T, typename opt_T = int32_t>
+	int32_t addValue(tgt_T* target, int32_t title_idx,
+	                 const char** texts, int32_t color,
+	                 eTextClass text_class, opt_T maximum,
+	                 int32_t left, int32_t top, int32_t width, int32_t height,
+	                 int32_t padding,
+	                 bool (*display_)(tgt_T* target, int32_t x, int32_t y) )
+	{
+		OptionItemBase* curr = nullptr;
+
+		if (target) {
+			try {
+				curr = new OptionItem<tgt_T, opt_T>(
+								target, "", title_idx,
+								nullptr, color, TC_NONE, 0, maximum, 1, nullptr,
+								menu_y + top, menu_x + left, width, height,
+								padding, display_);
+				this->setTexts(curr, texts, text_class);
+			} catch (std::bad_alloc &e) {
+				cerr << __FUNCTION__ << " : failed to allocate new OptionItem\n";
+				cerr << " [" << e.what() << "]" << endl;
+			}
+		}
+
+		return this->insert_option(curr, title_idx, nullptr);
+	}
+
+
+	/** @brief Value option with text array representation and action function
+	  *
+	  * Please note: The position @a left / @a top are relative to
+	  * the menu position.
+	  *
+	  * Please also note that a title is displayed to the left of the display
+	  * area.
+	  *
+	  * @param[in] target Pointer to the target to handle.
+	  * @param[in,out] action_ Pointer to the action function handling the wheel button click.
+	  * @param[in] title_idx Index of the title if it is listed in MenuTitleText.
+	  * @param[in] texts Free text array.
+	  * @param[in] color Regular display color of the title/target.
+	  * @param[in] text_class The text class, set to TC_FREETEXT to use @a texts.
+	  * @param[in] maximum Maximum value for ET_VALUE targets.
+	  * @param[in] left Relative left position of the display area to the menu.
+	  * @param[in] top Relative top position of the display area to the menu.
+	  * @param[in] width Width of the display area. The real width might be larger.
+	  * @param[in] height Height of the display area.
+	  * @param[in] padding Distance between title and display.
+	**/
+	template<typename tgt_T, typename opt_T = int32_t>
+	int32_t addValue(tgt_T* target,
+	                 int32_t (*action_)(tgt_T* target, int32_t val),
+	                 int32_t title_idx,
+	                 const char** texts, int32_t color,
+	                 eTextClass text_class, opt_T maximum,
+	                 int32_t left, int32_t top, int32_t width, int32_t height,
+	                 int32_t padding)
+	{
+		OptionItemBase* curr = nullptr;
+
+		if (target) {
+			try {
+				curr = new OptionItem<tgt_T, opt_T>(
+								target, action_, ET_VALUE,
+								"", title_idx, nullptr, color, TC_NONE,
+								0, maximum, 1, nullptr,
+								menu_y + top, menu_x + left, width, height,
+								padding, nullptr);
+				this->setTexts(curr, texts, text_class);
+			} catch (std::bad_alloc &e) {
+				cerr << __FUNCTION__ << " : failed to allocate new OptionItem\n";
+				cerr << " [" << e.what() << "]" << endl;
+			}
+		}
+
+		return this->insert_option(curr, title_idx, nullptr);
+	}
+
+
+	void            clearAll    (bool full_clear);
+	int32_t         count       ();
+	int32_t         delete_entry(int32_t index);
+	void            displayAll  (bool full_display);
+	void            distribute  (int32_t first_idx, int32_t last_idx,
+	                             int32_t list_width, int32_t list_height,
+	                             int32_t y_off, bool do_update);
+	OptionItemBase* getSelected ();
+	const char*     getTitle    () const;
+	void            move_entry  (int32_t from_idx, int32_t to_idx);
+	void            redraw      (int32_t index, bool update_full);
+	void            redrawAll   (bool full_redraw);
+	void            setLanguage (bool autorefresh);
+	void            setTitle    (const char* new_title, bool autorefresh);
+
+
+	/* ------------------------
+	 * --- Public operators ---
+	 * ------------------------
+	 */
+
+	// operator() to use a menu instance like a function
+	int32_t operator()();
+
+	// Get a stored option by index
+	OptionItemBase* operator[](int32_t index);
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	int32_t insert_option     (OptionItemBase* new_opt);
+	int32_t insert_option     (OptionItemBase* new_opt, int32_t title_idx,
+	                           const char* title_);
+	bool    is_title_idx_valid(int32_t title_idx);
+	int32_t selectClicked     (int32_t x, int32_t y);
+	void    selectNext        ();
+	void    selectPrev        ();
+	void    setTexts          (OptionItemBase* item, const char** texts,
+	                           eTextClass text_class);
+	void    unselect          ();
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	int32_t          bgItems    = 0;
+	int32_t          bgOffset   = 0;
+	eBackgroundTypes bgType     = BACKGROUND_BLANK;
+	int32_t          entry_cnt  = 0;          //!< Number of entries currently in the list.
+	int32_t          entry_sel  = -1;         //!< Currently selected entry or -1 if none is selected.
+	eMenuClass       menu_class = MC_MAIN;    //!< The class of the menu, decides upon what to display.
+	eLanguages       menu_lang  = EL_ENGLISH; //!< The language to display
+	int32_t          menu_x     = 0;          //!< X-Pos where the menu background starts
+	int32_t          menu_y     = 0;          //!< Y-Pos where the menu background starts
+	OptionItemBase*  root       = nullptr;    //!< The first menu item
+	OptionItemBase*  tail       = nullptr;    //!< The last menu item
+	const char*      title      = nullptr;    //!< Name/Title of the menu
+	uint32_t         title_len  = 0;          //!< Length of the menu title with the current font.
+	bool             title_set  = false;      //!< Set to true if this has been changed to be an individual title
+	int32_t          title_x    = 0;
 };
-typedef struct
-  {
-    const char	*name;
-    int	(*displayFunc) (ENVIRONMENT*, int, int, void*);
-    int	color;
-    double	*value;
-    void	*data;
-    const char	*format;
-    double	min, max;
-    double	increment;
-    double	defaultv;
-    char	**specialOpts;
-    char	type;
-    int	viewonly;
-    int	x;
-    int	y;
-  } MENUENTRY;
-
-typedef struct
-  {
-    const char *title;
-    int numEntries;
-    MENUENTRY *entries;
-    int quitButton;
-    int okayButton;
-  } MENUDESC;
-
-
-// Set the menus to appear in English or Portugese
-void Select_Menu_Language(GLOBALDATA *global);
-
-int options (GLOBALDATA *global, ENVIRONMENT *env, MENUDESC *menu);
-
-#endif
+#define MENU_CLASS_DECLARES 1
+
+
+// --- Helper functions for action/display usage that need optioncontent.h ---
+
+/// @brief display function to display the chosen tank at a specific location
+bool display_tank_desc(int32_t* tanknum, int32_t x, int32_t y);
+
+
+
+#endif // ATANKS_SRC_MENU_H_INCLUDED
+
diff --git a/src/menucontent.h b/src/menucontent.h
deleted file mode 100644
index 04c6a17..0000000
--- a/src/menucontent.h
+++ /dev/null
@@ -1,852 +0,0 @@
-#ifndef MENUCONTENT_HEADER_
-#define MENUCONTENT_HEADER_
-
-/*
- * atanks - obliterate each other with oversize weapons
- * Copyright (C) 2003  Thomas Hudson
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- * */
-
-char *onOffText[2] = { "Off", "On"};
-char *onOffRandomText[3] = { "Off", "On", "Random"};
-char *landSlideText[5] = { "None", "Tank Only", "Instant", "Gravity", "Cartoon"};
-char *wallTypeText[5] = { "Rubber", "Steel", "Spring", "Wrap", "Random"};
-char *mouseText[2] = { "Custom", "Default"};
-char *meteorText[4] = { "Off", "Light", "Heavy", "Lethal"};
-char *lightningText[4] = { "Off", "Weak", "Energetic", "Violent"};
-char *laserSatelliteText[4] = { "Off", "Weak", "Strong", "Super"};
-char *languageText[8] = { "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian"};
-char *colourText[2] = { "Regular", "Crispy"};
-char *landTypeText[8] = { "Random", "Canyons", "Mountains", "Valleys", "Hills", "Foothills", "Plains", "None" };
-char *turnTypeText[4] = { "High+", "Low+", "Random", "Simul" };
-char *skipTypeText[2] = { "Off", "Humans Dead"};
-char *soundDriver[6] = { "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK" };
-
-// portuege version
-char *onOffText_ptbr[2] = { "Desligado", "Ligado"};
-/*translate*/
-char *onOffRandomText_ptbr[3] = { "Desligado", "Ligado", "Aleatório"};
-char *landSlideText_ptbr[5] = { "Nenhum", "Tanque Somente", "Instantâneo", "Gravidade", "Cartoon"};
-char *wallTypeText_ptbr[5] = { "Elástico", "Aço", "Mola", "Envoltório", "Aleatório"};
-char *mouseText_ptbr[2] = { "Personalizado", "Padrão"};
-char *meteorText_ptbr[4] = { "Desligado", "Fraco", "Forte", "Letal"};
-char *lightningText_ptbr[4] = { "Desligado", "Fraco", "Energético", "Violento"};
-char *laserSatelliteText_ptbr[4] = { "Desligado", "Fraco", "Forte", "Super"};
-char *languageText_ptbr[8] = { "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian"};
-char *colourText_ptbr[2] = { "Regular", "Crispy"};
-char *landTypeText_ptbr[8] = { "Aleatório", "Canyons", "Montanhas", "Vales", "Colinas", "Morros", "Planos", "Nenhum" };
-char *turnTypeText_ptbr[4] = { "Melhores+", "Piores+", "Aleatório", "Simular" };
-char *skipTypeText_ptbr[2] = { "Sim", "Não" };
-
-
-// french version
-char *onOffText_fr[2] = { "Non", "Oui"};
-/*translate*/
-char *onOffRandomText_fr[3] = { "Non", "Oui", "Hasard"};
-char *landSlideText_fr[5] = { "Aucun", "Réservoir Seulement", "Instantané", "Gravité", "Dessin animé"};
-char *wallTypeText_fr[5] = { "Elastique", "Acier", "Mou", "Enveloppe", "Aléatoire"};
-char *mouseText_fr[2] = { "Pesonnel", "Défaut"};
-char *meteorText_fr[4] = { "Off", "Light", "Heavy", "Lethal"};
-char *lightningText_fr[4] = { "Aucun", "Faible", "Energique", "Violent"};
-char *laserSatelliteText_fr[4] = { "Aucun", "Faible", "Fort", "Super"};
-char *languageText_fr[8] = { "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian"};
-char *colourText_fr[2] = { "Régulier", "Croustillant"};
-char *landTypeText_fr[8] = { "Aléatoire", "Canyons", "Montagnes", "Vallées", "Collines", "Contreforts", "Plaines", "Aucun" };
-char *turnTypeText_fr[4] = { "Haut", "Bas", "Aléatoire", "Similaire" };
-char *skipTypeText_fr[2] = { "Oui", "Non"};
-
-
-// german version
-char *onOffText_de[2] = { "Aus", "An"};
-char *onOffRandomText_de[3] = { "Aus", "An", "Zufällig"};
-char *landSlideText_de[5] = { "Keine", "Nur Panzer", "Sofort", "Schwerkraft", "Cartoon"};
-char *wallTypeText_de[5] = { "Gummi", "Stahl", "Federnd", "Verbunden", "Zufällig"};
-char *mouseText_de[2] = { "Angepasst", "Standard"};
-char *meteorText_de[4] = { "Aus", "Leicht", "Schwer", "Tödlich"};
-char *lightningText_de[4] = { "Aus", "Schwach", "Stark", "Brutal"};
-char *laserSatelliteText_de[4] = { "Aus", "Schwach", "Stark", "Super"};
-char *languageText_de[8] = { "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian"};
-char *colourText_de[2] = { "Normal", "Kontrastreich"};
-char *landTypeText_de[8] = { "Zufällig", "Canyons", "Berge", "Täler", "Hügel", "Flache Hügel", "Ebene", "Nichts" };
-char *turnTypeText_de[4] = { "Hoch+", "Niedrig+", "Zufällig", "Simul" };
-char *skipTypeText_de[2] = { "Aus", "An"};
-
-// slovak version
-char *onOffText_sk[2] = { "Vypnuté", "Zapnuté"};
-char *onOffRandomText_sk[3] = { "Vypnutý", "Zapnutý", "Náhodný"};
-char *landSlideText_sk[5] = { "Žiaden", "Iba tank", "Okamžitý", "Gravitácia", "Kresl.film"};
-char *wallTypeText_sk[5] = { "Guma", "Oceľ", "Pružina", "Prikrývka", "Náhodný"};
-char *mouseText_sk[2] = { "Vlastné", "Východzie"};
-char *meteorText_sk[4] = { "Vypnuté", "Ľahké", "Ťažké", "Smrteľné"};
-char *lightningText_sk[4] = { "Vypnuté", "Slabé", "Energetické", "Kruté"};
-char *laserSatelliteText_sk[4] = { "Vypnutý", "Slabý", "Silný", "Super"};
-char *languageText_sk[8] = { "Anglicky", "Portugalsky", "Francúzsky", "Nemecky", "Slovensky", "Rusky", "Spanish", "Italian"};
-char *colourText_sk[2] = { "Normálna", "Svieža"};
-char *landTypeText_sk[8] = { "Náhodná", "Kaňony", "Hory", "Údolia", "Kopce", "Úpätia", "Nížiny", "Žiadna" };
-char *turnTypeText_sk[4] = { "Vysoký+", "Nízky+", "Náhodný", "Simul" };
-char *skipTypeText_sk[2] = { "Vypnuté", "Smrť ľudí"};
-
-// Russian version
-char *onOffText_ru[2] = { "Выкл.", "Вкл."};
-char *onOffRandomText_ru[3] = { "Выкл.", "Вкл.", "Случайно"};
-char *landSlideText_ru[5] = { "Выкл.", "Только танки", "Сразу же", "По умолчанию", "Как в мультиках"};
-char *wallTypeText_ru[5] = { "Резиновые", "Непробиваемые", "Пружинящие", "Бесконечность", "Случайные"};
-char *mouseText_ru[2] = { "Собственный", "По умолчанию"};
-char *meteorText_ru[4] = { "Нет", "Слабый", "Сильный", "Смертельный"};
-char *lightningText_ru[4] = { "Нет", "Слабые", "Сильные", "Мощные"};
-char *laserSatelliteText_ru[4] = { "Нет", "Слабые", "Сильные", "Супер!!"};
-char *languageText_ru[8] = { "English", "Português", "Français", "Deutsch", "Slovak", "Русский", "Spanish", "Italian"};
-char *colourText_ru[2] = { "Обычная", "Четкая"};
-char *landTypeText_ru[8] = { "Случайный", "Каньоны", "Горы", "Возвышенность", "Холмы", "Предгорья", "Равнины", "Выкл." };
-char *turnTypeText_ru[4] = { "Сильные +", "Слабые +", "Случайно", "Все сразу" };
-char *skipTypeText_ru[2] = { "Выкл.", "Вкл."};
-
-
-
-
-// declare variables
-MENUDESC mainMenu;
-
-if ( (global->language == LANGUAGE_ENGLISH) || (global->language == LANGUAGE_SPANISH) || (global->language == LANGUAGE_ITALIAN) )
-  {
-    static MENUENTRY physicsOpts[8] =
-    {
-      { "Gravity", NULL, WHITE, &env->gravity, NULL, "%2.3f", .025, .325, 0.025, .075, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 80},
-      { "Viscosity", NULL, WHITE, &env->viscosity, NULL, "%2.2f", .25, 2.0, 0.25, 1.0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 60},
-      { "Land Slide", NULL, WHITE, &env->landSlideType, NULL, "%s", 0, 4, 1, 3, landSlideText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Land Slide Delay", NULL, WHITE, &env->landSlideDelay, NULL, "%4.0f", 1, 5, 1, 3, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 20},
-      { "Wall Type", NULL, WHITE, &env->wallType, NULL, "%s", 0, 4, 1, 1, wallTypeText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight},
-      { "Boxed Mode", NULL, WHITE, &env->dBoxedMode, NULL, "%s", 0, 2, 1, 0, onOffRandomText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 20},
-      { "Violent Death", NULL, WHITE, &global->violent_death, NULL, "%s", 0, 3, 1, 0, lightningText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 40},
-      { "Timed Shots", NULL, WHITE, &global->max_fire_time, NULL, "%3.0f", 0, 180, 5, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 60}
-    };
-    MENUDESC physicsMenu = { "Physics", 8, physicsOpts, TRUE, FALSE};
-
-    static MENUENTRY weatherOpts[7] =
-    {
-      { "Meteor Showers", NULL, WHITE, &env->meteors, NULL, "%s", 0, 3, 1, 0, meteorText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Lightning", NULL, WHITE, &env->lightning, NULL, "%s", 0, 3, 1, 0, lightningText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Falling Dirt", NULL, WHITE, &env->falling_dirt_balls, NULL, "%s", 0, 3, 1, 0, meteorText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Laser Satellite", NULL, WHITE, &env->satellite, NULL, "%s", 0, 3, 1, 0, laserSatelliteText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Fog", NULL, WHITE, &env->fog, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Max Wind Strength", NULL, WHITE, (double*)&env->windstrength, NULL, "%2.0f", 0, 100, 5, 40, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Wind Variation", NULL, WHITE, (double*)&env->windvariation, NULL, "%2.1f", 0, 100, 3, 10, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-    };
-    MENUDESC weatherMenu = { "Weather", 7, weatherOpts, TRUE, FALSE};
-
-
-    static MENUENTRY soundOpts[3] =
-    {
-      { "All Sound", NULL, WHITE, &global->sound, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Sound Driver", NULL, WHITE, &global->sound_driver, NULL, "%s", 0, 5, 1, 0, soundDriver, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Music", NULL, WHITE, &global->play_music, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28}
-    };
-   MENUDESC soundMenu = { "Sound", 3, soundOpts, TRUE, FALSE};
-
-
-    static MENUENTRY graphicsOpts[12] =
-    {
-      { "Full Screen", NULL, WHITE, &global->full_screen, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 108},
-      { "Dithering", NULL, WHITE, &global->ditherGradients, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Detailed Land", NULL, WHITE, &global->detailedLandscape, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Detailed Sky", NULL, WHITE, &global->detailedSky, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Fading Text", NULL, WHITE, &env->dFadingText, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Shadowed Text", NULL, WHITE, &env->dShadowedText, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Colour Theme", NULL, WHITE, &global->colour_theme, NULL, "%s", 0, 1, 1, 1, colourText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Screen Width", NULL, WHITE, &global->temp_screenWidth, NULL, "%4.0f", 800, 1600, 200, 800, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Screen Height", NULL, WHITE, &global->temp_screenHeight, NULL, "%4.0f", 600, 1200, 200, 600, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Mouse Pointer", NULL, WHITE, &global->os_mouse, NULL, "%s", 0, 1, 1, 1, mouseText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 1, global->halfHeight + 72},
-      { "Game Speed", NULL, WHITE, &global->frames_per_second, NULL, "%3.0f", 30, 120, 5, 60, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Custom Background", NULL, WHITE, &env->custom_background, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC graphicsMenu = { "Graphics", 12, graphicsOpts, TRUE, FALSE};
-
-    static MENUENTRY financeOpts[9] =
-    {
-      { "Starting Money", NULL, WHITE, (double*)&global->startmoney, NULL, "%2.0f", 0, 200000, 5000, 20000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Interest Rate", NULL, WHITE, (double*)&global->interest, NULL, "%2.2f", 1.0, 1.5, 0.05, 1.25, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Round Win Bonus", NULL, WHITE, (double*)&global->scoreRoundWinBonus, NULL, "%2.0f", 0, 50000, 5000, 10000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Damage Bounty", NULL, WHITE, (double*)&global->scoreHitUnit, NULL, "%2.0f", 0, 500, 25, 75, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Self-Damage Penalty", NULL, WHITE, (double*)&global->scoreSelfHit, NULL, "%2.0f", 0, 10000, 1000, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Tank Destruction Bonus", NULL, WHITE, (double*)&global->scoreUnitDestroyBonus, NULL, "%2.0f", 0, 20000, 2500, 5000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Tank Self-Destruction Penalty", NULL, WHITE, (double*)&global->scoreUnitSelfDestroy, NULL, "%2.0f", 0, 20000, 2500, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Item Sell Multiplier", NULL, WHITE, (double*)&global->sellpercent, NULL, "%1.2f", 0.0, 1.0, 0.10, 0.80, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth -3, global->halfHeight + 92},
-      { "Teams Share", NULL, WHITE, (double *) &global->divide_money, NULL, "%s", 0.0, 1.0, 1.0, 0.0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC financeMenu = { "Money", 9, financeOpts, TRUE, FALSE};
-
-    static MENUENTRY networkOpts[5] =
-    {
-       {  "Check Updates", NULL, WHITE, (double*) &global->check_for_updates, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-       {  "Networking", NULL, WHITE, (double*) &global->enable_network, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-       {  "Listen Port", NULL, WHITE, (double*) &global->listen_port, NULL, "%5.0f", 10645, 64645, 1000, 25645, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-       {  "Server address", NULL, WHITE, (double *) &(global->server_name), NULL, "%s", 0, 0, 0, 0, NULL, OPTION_TEXTTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12 },
-       { "Server port", NULL, WHITE, (double *) &(global->server_port), NULL, "%s", 0, 0, 0, 0, NULL, OPTION_TEXTTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32}
-    };
-    MENUDESC networkMenu = { "Network", 5, networkOpts, TRUE, FALSE};
-
-    void *pPhysicsMenu  =       &physicsMenu;
-    void *pWeatherMenu  =       &weatherMenu;
-    void *pGraphicsMenu =       &graphicsMenu;
-    void *pFinanceMenu  =       &financeMenu;
-    void *pnetworkMenu =        &networkMenu;
-    void *pSoundMenu   =        &soundMenu;
-
-    static MENUENTRY mainOpts[12] =
-    {
-      { "Physics", NULL, WHITE, (double*)pPhysicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Weather", NULL, WHITE, (double*)pWeatherMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Graphics", NULL, WHITE, (double*)pGraphicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Money", NULL, WHITE, (double*)pFinanceMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Network", NULL, WHITE, (double*)pnetworkMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Sound", NULL, WHITE, (double*) pSoundMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Weapon Tech Level", NULL, WHITE, (double*)&env->weapontechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Item Tech Level", NULL, WHITE, (double *) &env->itemtechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Landscape", NULL, WHITE, (double*)&env->landType, NULL, "%s", 0, 7, 1, LANDTYPE_HILLS, landTypeText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Turn Order", NULL, WHITE, (double*)&global->turntype, NULL, "%s", 0, 3, 1, TURN_RANDOM, turnTypeText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Skip AI-only play", NULL, WHITE, &global->skipComputerPlay, NULL, "%s", 0, 1, 1, 1, skipTypeText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112},
-      { "Language", NULL, WHITE, &global->language, NULL, "%s", 0, 7, 1, 0, languageText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 132}
-    };
-// mainMenu = { "Main Menu", 10, mainOpts, TRUE, FALSE};
-    mainMenu.title = "Main Menu";
-    mainMenu.numEntries = 12;
-    mainMenu.entries = mainOpts;
-    mainMenu.quitButton = TRUE;
-    mainMenu.okayButton = FALSE;
-
-  }  // end of English
-
-if (global->language == LANGUAGE_PORTUGUESE)  // Portuguese
-  {
-    static MENUENTRY physicsOpts[8] =
-    {
-      { "Gravidade", NULL, WHITE, &env->gravity, NULL, "%2.3f", .025, .325, 0.025, .075, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 80},
-      { "Viscosidade", NULL, WHITE, &env->viscosity, NULL, "%2.2f", .25, 2.0, 0.25, 1.0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 60},
-      { "Deslizamento de Terra", NULL, WHITE, &env->landSlideType, NULL, "%s", 0, 4, 1, 3, landSlideText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Corrediça da terra atrasa", NULL, WHITE, &env->landSlideDelay, NULL, "%4.0f", 1, 5, 1, 3, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 20},
-      { "Tipo de Parede", NULL, WHITE, &env->wallType, NULL, "%s", 0, 4, 1, 1, wallTypeText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight},
-      { "Modalidade encaixotada", NULL, WHITE, &env->dBoxedMode, NULL, "%s", 0, 2, 1, 0, onOffRandomText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 20},
-      { "Morte violenta", NULL, WHITE, &global->violent_death, NULL, "%s", 0, 3, 1, 0, lightningText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 40},
-      { "Tiro programado", NULL, WHITE, &global->max_fire_time, NULL, "%3.0f", 0, 180, 5, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 60}
-
-    };
-    MENUDESC physicsMenu = { "Física", 7, physicsOpts, TRUE, FALSE};
-
-    static MENUENTRY weatherOpts[7] =
-    {
-      { "Chuvas de Meteoro", NULL, WHITE, &env->meteors, NULL, "%s", 0, 3, 1,
-        0, meteorText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 68},
-      { "Relâmpagos", NULL, WHITE, &env->lightning, NULL, "%s", 0, 3, 1, 0,
-        lightningText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 48},
-      { "Sujeira de queda", NULL, WHITE, &env->falling_dirt_balls, NULL, "%s", 0, 3, 1, 0, meteorText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Satélite do Laser", NULL, WHITE, &env->satellite, NULL, "%s", 0, 3, 1, 0,
-        laserSatelliteText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 8},
-      { "Neblina", NULL, WHITE, &env->fog, NULL, "%s", 0, 1, 1,
-        0, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight + 12},
-      { "Velocidade Max do Vento", NULL, WHITE, (double*)&env->windstrength,
-        NULL, "%2.0f", 0, 100, 5, 40, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 32 },
-      { "Variação do Vento", NULL, WHITE, (double*)&env->windvariation,
-        NULL, "%2.1f", 0, 100, 3, 10, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 52},
-    };
-    MENUDESC weatherMenu = { "Condições Meteorológicas", 7, weatherOpts,
-                             TRUE, FALSE
-                           };
-
-    static MENUENTRY soundOpts[3] =
-    {
-      { "Efeitos de Som", NULL, WHITE, &global->sound, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Sistema de Som", NULL, WHITE, &global->sound_driver, NULL, "%s", 0, 5, 1, 0, soundDriver, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Música", NULL, WHITE, &global->play_music, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28}
-    };
-   MENUDESC soundMenu = { "Som", 3, soundOpts, TRUE, FALSE};
-
-
-    static MENUENTRY graphicsOpts[12] =
-    {
-      { "Tela Cheia", NULL, WHITE, &global->full_screen, NULL, "%s", 0, 1, 1, 0, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 108},
-      { "Pontilhamento", NULL, WHITE, &global->ditherGradients, NULL, "%s",
-        0, 1, 1, 1, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 88},
-      { "Detalhes do Terreno", NULL, WHITE, &global->detailedLandscape, NULL,
-        "%s", 0, 1, 1, 1, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight - 68},
-      { "Detalhes do Céu", NULL, WHITE, &global->detailedSky, NULL, "%s", 0,
-        1, 1, 1, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 48},
-      { "texto sombreado", NULL, WHITE, &env->dFadingText, NULL, "%s", 0, 1, 1, 1, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "texto de desvanecimento", NULL, WHITE, &env->dShadowedText, NULL, "%s", 0, 1, 1, 1, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "tema da cor", NULL, WHITE, &global->colour_theme, NULL, "%s", 0, 1, 1, 1, colourText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-
-      { "Largura da Tela", NULL, WHITE, &global->temp_screenWidth,
-        NULL, "%4.0f", 800, 1600, 200, 800, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 32 },
-      { "Altura da Tela", NULL, WHITE, &global->temp_screenHeight,
-        NULL, "%4.0f", 600, 1200, 200, 600, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 52},
-      { "Ponteiro do Rato", NULL, WHITE, &global->os_mouse, NULL,
-        "%s", 0, 1, 1, 1, mouseText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth
-        - 1, global->halfHeight + 72},
-      { "Velocidade do jogo", NULL, WHITE, &global->frames_per_second, NULL, "%3.0f", 30, 120, 5, 60, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Fundo personalizado", NULL, WHITE, &env->custom_background, NULL, "%s", 0, 1, 1, 0, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-
-    };
-    MENUDESC graphicsMenu = { "Gráficos", 12, graphicsOpts, TRUE, FALSE};
-
-    static MENUENTRY financeOpts[9] =
-    {
-      { "Dinheiro inicial", NULL, WHITE, (double*)&global->startmoney, NULL,
-        "%2.0f", 0, 200000, 5000, 20000, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight - 68},
-      { "Taxa de Juros", NULL, WHITE, (double*)&global->interest, NULL,
-        "%2.2f", 1.0, 1.5, 0.05, 1.25, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight - 48},
-      { "Bônus por Vitória", NULL, WHITE,
-        (double*)&global->scoreRoundWinBonus, NULL, "%2.0f", 0, 50000, 5000, 10000, NULL, OPTION_DOUBLETYPE,
-        FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Bônus por Estrago", NULL, WHITE, (double*)&global->scoreHitUnit,
-        NULL, "%2.0f", 0, 500, 25, 75, NULL, OPTION_DOUBLETYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 12},
-      { "Penalidade por Auto-Estrago", NULL, WHITE,
-        (double*)&global->scoreSelfHit, NULL, "%2.0f", 0, 10000, 1000, 0, NULL, OPTION_DOUBLETYPE,
-        FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Bônus por Tanque Destruído", NULL, WHITE,
-        (double*)&global->scoreUnitDestroyBonus, NULL, "%2.0f", 0, 20000, 2500, 5000, NULL,
-        OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Penalidade por Auto-Destruição", NULL, WHITE,
-        (double*)&global->scoreUnitSelfDestroy, NULL, "%2.0f", 0, 20000, 2500, 0, NULL,
-        OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Multiplicador de Item Vendido", NULL, WHITE,
-        (double*)&global->sellpercent, NULL, "%1.2f", 0.0, 1.0, 0.10, 0.80, NULL, OPTION_DOUBLETYPE,
-        FALSE, global->halfWidth -3, global->halfHeight + 92},
-      { "Parte das equipes", NULL, WHITE, (double *) &global->divide_money, NULL, "%s", 0.0, 1.0, 1.0, 0.0, onOffText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-
-    };
-    MENUDESC financeMenu = { "Dinheiro", 9, financeOpts, TRUE, FALSE};
-
-    static MENUENTRY networkOpts[3] =
-    {
-       {  "Procurar actualizações", NULL, WHITE, (double*) &global->check_for_updates, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-       {  "Activar Rede", NULL, WHITE, (double*) &global->enable_network, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-       {  "Número de Porta", NULL, WHITE, (double*) &global->listen_port, NULL, "%5.0f", 10645, 64645, 1000, 25645, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8}
-    };
-    MENUDESC networkMenu = { "Network", 3, networkOpts, TRUE, FALSE};
-
-
-    void *pPhysicsMenu  =       &physicsMenu;
-    void *pWeatherMenu  =       &weatherMenu;
-    void *pGraphicsMenu =       &graphicsMenu;
-    void *pFinanceMenu  =       &financeMenu;
-    void *pnetworkMenu  =       &networkMenu;
-    void *pSoundMenu    =       &soundMenu;
-
-    static MENUENTRY mainOpts[12] =
-    {
-      { "Física", NULL, WHITE, (double*)pPhysicsMenu, NULL, NULL, 0, 0, 0,
-        0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 88},
-      { "Condições Meteorológicas", NULL, WHITE, (double*)pWeatherMenu,
-        NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth -
-        3, global->halfHeight - 68},
-      { "Gráficos", NULL, WHITE, (double*)pGraphicsMenu, NULL, NULL, 0, 0,
-        0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 48},
-      { "Finanças", NULL, WHITE, (double*)pFinanceMenu, NULL, NULL, 0, 0, 0,
-        0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3,
-        global->halfHeight - 28},
-       { "Rede", NULL, WHITE, (double*)pnetworkMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Som", NULL, WHITE, (double*) pSoundMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Arma Nível Tecnológico", NULL, WHITE, (double*)&env->weapontechLevel, NULL,
-        "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth -
-        3, global->halfHeight + 32},
-      { "Artigo Nível Tecnológico", NULL, WHITE, (double*) &env->itemtechLevel, NULL,
-        "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Cenário", NULL, WHITE, (double*)&env->landType, NULL, "%s", 0, 7,
-        1, LANDTYPE_HILLS, landTypeText_ptbr, OPTION_SPECIALTYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 72},
-      { "Ordem de Jogadas", NULL, WHITE, (double*)&global->turntype, NULL,
-        "%s", 0, 3, 1, TURN_RANDOM, turnTypeText_ptbr, OPTION_SPECIALTYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 92},
-      { "Continuar o Jogo Só com Robôs", NULL, WHITE, &global->skipComputerPlay,
-        NULL, "%s", 0, 2, 1, 1, skipTypeText_ptbr, OPTION_SPECIALTYPE, FALSE,
-        global->halfWidth - 3, global->halfHeight + 112},
-      { "Língua", NULL, WHITE, &global->language, NULL, "%s", 0, 7, 1, 0, languageText_ptbr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 132}
-
-    };
-// mainMenu = { "Menu Principal", 10, mainOpts, TRUE, FALSE};
-    mainMenu.title = "Menu Principal";
-    mainMenu.numEntries = 12;
-    mainMenu.entries = mainOpts;
-    mainMenu.quitButton = TRUE;
-    mainMenu.okayButton = FALSE;
-
-  }   // end of Portuguese
-
-
-// french
-if (global->language == LANGUAGE_FRENCH)
-  {
-    static MENUENTRY physicsOpts[8] =
-    {
-      { "Gravité", NULL, WHITE, &env->gravity, NULL, "%2.3f", .025, .325, 0.025, .075, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 80},
-      { "Viscosité", NULL, WHITE, &env->viscosity, NULL, "%2.2f", .25, 2.0, 0.25, 1.0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Glissements de terrain", NULL, WHITE, &env->landSlideType, NULL, "%s", 0, 4, 1, 3, landSlideText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Délai glissements de terrain", NULL, WHITE, &env->landSlideDelay, NULL, "%4.0f", 1, 5, 1, 3, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 20},
-      { "Murs", NULL, WHITE, &env->wallType, NULL, "%s", 0, 4, 1, 1, wallTypeText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight},
-      { "Enfermé dans boîte", NULL, WHITE, &env->dBoxedMode, NULL, "%s", 0, 2, 1, 0, onOffRandomText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 20},
-      { "Mort violente", NULL, WHITE, &global->violent_death, NULL, "%s", 0, 3, 1, 0, lightningText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 40},
-      { "Projectile synchronisé", NULL, WHITE, &global->max_fire_time, NULL, "%3.0f", 0, 180, 5, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 60}
-
-    };
-    MENUDESC physicsMenu = { "Physique", 7, physicsOpts, TRUE, FALSE};
-
-    static MENUENTRY weatherOpts[7] =
-    {
-      { "Orages de météorites", NULL, WHITE, &env->meteors, NULL, "%s", 0, 3, 1, 0, meteorText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Éclairs", NULL, WHITE, &env->lightning, NULL, "%s", 0, 3, 1, 0, lightningText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Saleté en chute", NULL, WHITE, &env->falling_dirt_balls, NULL, "%s", 0, 3, 1, 0, meteorText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Satellites Laser", NULL, WHITE, &env->satellite, NULL, "%s", 0, 3, 1, 0, laserSatelliteText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Brouillard", NULL, WHITE, &env->fog, NULL, "%s", 0, 1, 1, 0, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Force maxi du vent", NULL, WHITE, (double*)&env->windstrength, NULL, "%2.0f", 0, 100, 5, 40, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Variation du vent", NULL, WHITE, (double*)&env->windvariation, NULL, "%2.1f", 0, 100, 3, 10, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-    };
-    MENUDESC weatherMenu = { "Météo", 7, weatherOpts, TRUE, FALSE};
-
-    static MENUENTRY soundOpts[3] =
-    {
-      { "Effets Sonores", NULL, WHITE, &global->sound, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Système de Son", NULL, WHITE, &global->sound_driver, NULL, "%s", 0, 5, 1, 0, soundDriver, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Musique", NULL, WHITE, &global->play_music, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28}
-    };
-   MENUDESC soundMenu = { "Sound", 3, soundOpts, TRUE, FALSE};
-
-
-    static MENUENTRY graphicsOpts[12] =
-    {
-      { "Full Screen", NULL, WHITE, &global->full_screen, NULL, "%s", 0, 1, 1, 0, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 108},
-      { "Tramage", NULL, WHITE, &global->ditherGradients, NULL, "%s", 0, 1, 1, 1, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Détails du terrain", NULL, WHITE, &global->detailedLandscape, NULL, "%s", 0, 1, 1, 1, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Ciel détaillé", NULL, WHITE, &global->detailedSky, NULL, "%s", 0, 1, 1, 1, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "texte ombragé", NULL, WHITE, &env->dFadingText, NULL, "%s", 0, 1, 1, 1, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "texte de effacement", NULL, WHITE, &env->dShadowedText, NULL, "%s", 0, 1, 1, 1, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Thème de couleurs", NULL, WHITE, &global->colour_theme, NULL, "%s", 0, 1, 1, 1, colourText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Largeur d'écran", NULL, WHITE, &global->temp_screenWidth, NULL, "%4.0f", 800, 1600, 200, 800, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Hauteur d'écran", NULL, WHITE, &global->temp_screenHeight, NULL, "%4.0f", 600, 1200, 200, 600, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Curseur de souris", NULL, WHITE, &global->os_mouse, NULL, "%s", 0, 1, 1, 1, mouseText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 1, global->halfHeight + 72},
-      { "Vitesse du jeu", NULL, WHITE, &global->frames_per_second, NULL, "%3.0f", 30, 120, 5, 60, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Fond fait sur commande", NULL, WHITE, &env->custom_background, NULL, "%s", 0, 1, 1, 0, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC graphicsMenu = { "Graphismes", 12, graphicsOpts, TRUE, FALSE};
-
-    static MENUENTRY financeOpts[9] =
-    {
-      { "Somme de départ", NULL, WHITE, (double*)&global->startmoney, NULL, "%2.0f", 0, 200000, 5000, 20000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Taux d'intérêt", NULL, WHITE, (double*)&global->interest, NULL, "%2.2f", 1.0, 1.5, 0.05, 1.25, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Gains par victoire", NULL, WHITE, (double*)&global->scoreRoundWinBonus, NULL, "%2.0f", 0, 50000, 5000, 10000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Bonus dommages", NULL, WHITE, (double*)&global->scoreHitUnit, NULL, "%2.0f", 0, 500, 25, 75, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Pénalité auto-dommages", NULL, WHITE, (double*)&global->scoreSelfHit, NULL, "%2.0f", 0, 10000, 1000, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Bonus destruction tank", NULL, WHITE, (double*)&global->scoreUnitDestroyBonus, NULL, "%2.0f", 0, 20000, 2500, 5000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Pénalité autodestruction tank", NULL, WHITE, (double*)&global->scoreUnitSelfDestroy, NULL, "%2.0f", 0, 20000, 2500, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Coeff. vente item", NULL, WHITE, (double*)&global->sellpercent, NULL, "%1.2f", 0.0, 1.0, 0.10, 0.80, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth -3, global->halfHeight + 92},
-      { "Part d'equipes", NULL, WHITE, (double *) &global->divide_money, NULL, "%s", 0.0, 1.0, 1.0, 0.0, onOffText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-
-    };
-    MENUDESC financeMenu = { "Finances", 9, financeOpts, TRUE, FALSE};
-
-    static MENUENTRY networkOpts[3] =
-    {
-       {  "Check Updates", NULL, WHITE, (double*) &global->check_for_updates, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-       {  "Networking", NULL, WHITE, (double*) &global->enable_network, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-       {  "Listen Port", NULL, WHITE, (double*) &global->listen_port, NULL, "%5.0f", 10645, 64645, 1000, 25645, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8}
-    };
-    MENUDESC networkMenu = { "Network", 3, networkOpts, TRUE, FALSE};
-
-
-
-    void *pPhysicsMenu  =       &physicsMenu;
-    void *pWeatherMenu  =       &weatherMenu;
-    void *pGraphicsMenu =       &graphicsMenu;
-    void *pFinanceMenu  =       &financeMenu;
-    void *pnetworkMenu  =       &networkMenu;
-    void *pSoundMenu    =       &soundMenu;
-
-    static MENUENTRY mainOpts[12] =
-    {
-      { "Physique", NULL, WHITE, (double*)pPhysicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Météo", NULL, WHITE, (double*)pWeatherMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Graphismes", NULL, WHITE, (double*)pGraphicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Finances", NULL, WHITE, (double*)pFinanceMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Réseau", NULL, WHITE, (double*)pnetworkMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Sound", NULL, WHITE, (double*) pSoundMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Niveau technique armes", NULL, WHITE, (double*)&env->weapontechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Niveau technique équipement", NULL, WHITE, (double *) &env->itemtechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Paysage", NULL, WHITE, (double*)&env->landType, NULL, "%s", 0, 7, 1, LANDTYPE_HILLS, landTypeText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Ordre de passage", NULL, WHITE, (double*)&global->turntype, NULL, "%s", 0, 3, 1, TURN_RANDOM, turnTypeText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Continuer le Jeu Robots seuls", NULL, WHITE, &global->skipComputerPlay, NULL, "%s", 0, 1, 1, 1, skipTypeText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112},
-      { "Langue", NULL, WHITE, &global->language, NULL, "%s", 0, 7, 1, 0, languageText_fr, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 132}
-    };
-// mainMenu = { "Menu principal", 10, mainOpts, TRUE, FALSE};
-    mainMenu.title = "Menu principal";
-    mainMenu.numEntries = 12;
-    mainMenu.entries = mainOpts;
-    mainMenu.quitButton = TRUE;
-    mainMenu.okayButton = FALSE;
-
-
-  }       // end of french
-
-if (global->language == LANGUAGE_GERMAN)
-  {
-    static MENUENTRY physicsOpts[8] =
-    {
-      { "Gravitation", NULL, WHITE, &env->gravity, NULL, "%2.3f", .025, .325, 0.025, .075, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 80},
-      { "Reibung", NULL, WHITE, &env->viscosity, NULL, "%2.2f", .25, 2.0, 0.25, 1.0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 60},
-      { "Erdrutsch", NULL, WHITE, &env->landSlideType, NULL, "%s", 0, 4, 1, 3, landSlideText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Erdrutsch Verzögerung", NULL, WHITE, &env->landSlideDelay, NULL, "%4.0f", 1, 5, 1,3, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 20},
-      { "Wand Art", NULL, WHITE, &env->wallType, NULL, "%s", 0, 4, 1, 1, wallTypeText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight},
-      { "Höhlenmodus", NULL, WHITE, &env->dBoxedMode, NULL, "%s", 0, 2, 1, 0, onOffRandomText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 20},
-      { "Gewalttätiger Tod", NULL, WHITE, &global->violent_death, NULL, "%s", 0, 3, 1, 0, lightningText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 40},
-      { "Zeitlimit", NULL, WHITE, &global->max_fire_time, NULL, "%3.0f", 0, 180, 5, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 60}
-    };
-    MENUDESC physicsMenu = { "Physik", 8, physicsOpts, TRUE, FALSE};
-
-    static MENUENTRY weatherOpts[7] =
-    {
-      { "Meteoritenregen", NULL, WHITE, &env->meteors, NULL, "%s", 0, 3, 1, 0, meteorText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Gewitter", NULL, WHITE, &env->lightning, NULL, "%s", 0, 3, 1, 0, lightningText_de,OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Schmutzregen", NULL, WHITE, &env->falling_dirt_balls, NULL, "%s", 0, 3, 1, 0, meteorText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Lasersatellit", NULL, WHITE, &env->satellite, NULL, "%s", 0, 3, 1, 0, laserSatelliteText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Nebel", NULL, WHITE, &env->fog, NULL, "%s", 0, 1, 1, 0, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Max Windstärke", NULL, WHITE, (double*)&env->windstrength, NULL, "%2.0f", 0, 100, 5, 40, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Windveränderung", NULL, WHITE, (double*)&env->windvariation, NULL, "%2.1f", 0, 100, 3, 10, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-    };
-    MENUDESC weatherMenu = { "Wetter", 7, weatherOpts, TRUE, FALSE};
-
-    static MENUENTRY soundOpts[3] =
-    {
-      { "Alle Sounds", NULL, WHITE, &global->sound, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Sound Treiber", NULL, WHITE, &global->sound_driver, NULL, "%s", 0, 5, 1, 0, soundDriver, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Musik", NULL, WHITE, &global->play_music, NULL, "%s", 0, 1, 1, 0, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28}
-    };
-   MENUDESC soundMenu = { "Sounds", 3, soundOpts, TRUE, FALSE};
-
-
-
-    static MENUENTRY graphicsOpts[12] =
-    {
-      { "Full Screen", NULL, WHITE, &global->full_screen, NULL, "%s", 0, 1, 1, 0, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 108},
-      { "Dithering", NULL, WHITE, &global->ditherGradients, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Landdetails", NULL, WHITE, &global->detailedLandscape, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Himmeldetails", NULL, WHITE, &global->detailedSky, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Ausblendender Text", NULL, WHITE, &env->dFadingText, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Schattierter Text", NULL, WHITE, &env->dShadowedText, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Farbschema", NULL, WHITE, &global->colour_theme, NULL, "%s", 0, 1, 1, 1, colourText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Bildschirmbreite", NULL, WHITE, &global->temp_screenWidth, NULL, "%4.0f", 800, 1600, 200, 800, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Bildschirmhöhe", NULL, WHITE, &global->temp_screenHeight, NULL, "%4.0f", 600, 1200, 200, 600, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Mauszeiger", NULL, WHITE, &global->os_mouse, NULL, "%s", 0, 1, 1, 1, mouseText_de,OPTION_SPECIALTYPE, FALSE, global->halfWidth - 1, global->halfHeight + 72},
-      { "Spielgeschwindigket", NULL, WHITE, &global->frames_per_second, NULL, "%3.0f", 30, 120, 5, 60, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "kundenspezifischer Hintergrund", NULL, WHITE, &env->custom_background, NULL, "%s", 0, 1, 1, 0, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC graphicsMenu = { "Grafik", 12, graphicsOpts, TRUE, FALSE};
-
-
-    static MENUENTRY financeOpts[9] =
-    {
-      { "Startgeld", NULL, WHITE, (double*)&global->startmoney, NULL, "%2.0f", 0, 200000, 5000, 20000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Zinssatz", NULL, WHITE, (double*)&global->interest, NULL, "%2.2f", 1.0, 1.5, 0.05,1.25, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Rundenbonus", NULL, WHITE, (double*)&global->scoreRoundWinBonus, NULL, "%2.0f", 0,50000, 5000, 10000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Schadensbonus", NULL, WHITE, (double*)&global->scoreHitUnit, NULL, "%2.0f", 0, 500, 25, 75, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Strafe für Selbstschaden", NULL, WHITE, (double*)&global->scoreSelfHit, NULL, "%2.0f", 0, 10000, 1000, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Zerstörungsbonus", NULL, WHITE, (double*)&global->scoreUnitDestroyBonus, NULL, "%2.0f", 0, 20000, 2500, 5000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Selbstzerstörungsstrafe", NULL, WHITE, (double*)&global->scoreUnitSelfDestroy, NULL, "%2.0f", 0, 20000, 2500, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Verkaufsmultiplikator", NULL, WHITE, (double*)&global->sellpercent, NULL, "%1.2f",0.0, 1.0, 0.10, 0.80, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth -3, global->halfHeight + 92},
-      { "Mannschaftanteil", NULL, WHITE, (double *) &global->divide_money, NULL, "%s", 0.0, 1.0, 1.0, 0.0, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-
-    };
-    MENUDESC financeMenu = { "Geld", 9, financeOpts, TRUE, FALSE};
-
-    static MENUENTRY networkOpts[3] =
-    {
-       {  "Auf Aktualisierungen prüfen", NULL, WHITE, (double*) &global->check_for_updates, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-       {  "Netzwerk", NULL, WHITE, (double*) &global->enable_network, NULL, "%s", 0, 1, 1, 1, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-       {  "offener Port", NULL, WHITE, (double*) &global->listen_port, NULL, "%5.0f", 10645, 64645, 1000, 25645, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8}
-    };
-    MENUDESC networkMenu = { "Netzwerk", 3, networkOpts, TRUE, FALSE};
-
-
-    void *pPhysicsMenu  =       &physicsMenu;
-    void *pWeatherMenu  =       &weatherMenu;
-    void *pGraphicsMenu =       &graphicsMenu;
-    void *pFinanceMenu  =       &financeMenu;
-    void *pnetworkMenu  =       &networkMenu;
-    void *pSoundMenu    =       &soundMenu;
-
-    static MENUENTRY mainOpts[12] =
-    {
-      { "Physik", NULL, WHITE, (double*)pPhysicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Wetter", NULL, WHITE, (double*)pWeatherMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Grafik", NULL, WHITE, (double*)pGraphicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Geld", NULL, WHITE, (double*)pFinanceMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Netzwerk", NULL, WHITE, (double*)pnetworkMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Sounds", NULL, WHITE, (double*) pSoundMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Technologiestufe Waffen", NULL, WHITE, (double*)&env->weapontechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Technologiestufe Gegenstände", NULL, WHITE, (double *) &env->itemtechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Landschaft", NULL, WHITE, (double*)&env->landType, NULL, "%s", 0, 7, 1, LANDTYPE_HILLS, landTypeText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight+ 72},
-      { "Reihenfolge", NULL, WHITE, (double*)&global->turntype, NULL, "%s", 0, 3, 1, TURN_RANDOM, turnTypeText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Überspringe Nur-KI", NULL, WHITE, &global->skipComputerPlay, NULL, "%s", 0, 1, 1, 1, skipTypeText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112},
-      { "Sprache", NULL, WHITE, &global->language, NULL, "%s", 0, 7, 1, 0, languageText_de,OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 132}
-    };
-// mainMenu = { "Hauptmenü", 10, mainOpts, TRUE, FALSE};
-    mainMenu.title = "Hauptmenü";
-    mainMenu.numEntries = 12;
-    mainMenu.entries = mainOpts;
-    mainMenu.quitButton = TRUE;
-    mainMenu.okayButton = FALSE;
-
-  }  // end of German
-
-
-if (global->language == LANGUAGE_SLOVAK)
-  {
-    static MENUENTRY physicsOpts[8] =
-    {
-      { "Gravitácia", NULL, WHITE, &env->gravity, NULL, "%2.3f", .025, .325, 0.025, .075, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 80},
-      { "Viskozita", NULL, WHITE, &env->viscosity, NULL, "%2.2f", .25, 2.0, 0.25, 1.0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 60},
-      { "Zosun zeme", NULL, WHITE, &env->landSlideType, NULL, "%s", 0, 4, 1, 3, landSlideText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Zdržanie zosunu zeme", NULL, WHITE, &env->landSlideDelay, NULL, "%4.0f", 1, 5, 1, 3, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 20},
-      { "Typ steny", NULL, WHITE, &env->wallType, NULL, "%s", 0, 4, 1, 1, wallTypeText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight},
-      { "Režim krabíc", NULL, WHITE, &env->dBoxedMode, NULL, "%s", 0, 2, 1, 0, onOffRandomText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 20},
-      { "Krutá smrť", NULL, WHITE, &global->violent_death, NULL, "%s", 0, 3, 1, 0, lightningText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 40},
-      { "Časované strely", NULL, WHITE, &global->max_fire_time, NULL, "%3.0f", 0, 180, 5, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 60}
-    };
-    MENUDESC physicsMenu = { "Fyzika", 7, physicsOpts, TRUE, FALSE};
-
-    static MENUENTRY weatherOpts[7] =
-    {
-      { "Dážď meteorov", NULL, WHITE, &env->meteors, NULL, "%s", 0, 3, 1, 0, meteorText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Blesky", NULL, WHITE, &env->lightning, NULL, "%s", 0, 3, 1, 0, lightningText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Padajúca zem", NULL, WHITE, &env->falling_dirt_balls, NULL, "%s", 0, 3, 1, 0, meteorText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Laserový satelit", NULL, WHITE, &env->satellite, NULL, "%s", 0, 3, 1, 0, laserSatelliteText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Hmla", NULL, WHITE, &env->fog, NULL, "%s", 0, 1, 1, 0, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Maximálna sila vetra", NULL, WHITE, (double*)&env->windstrength, NULL, "%2.0f", 0, 100, 5, 40, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Zmena vetra", NULL, WHITE, (double*)&env->windvariation, NULL, "%2.1f", 0, 100, 3, 10, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-    };
-    MENUDESC weatherMenu = { "Počasie", 7, weatherOpts, TRUE, FALSE};
-
-    static MENUENTRY soundOpts[3] =
-    {
-      { "Všetky zvuky", NULL, WHITE, &global->sound, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Ovládač zvuku", NULL, WHITE, &global->sound_driver, NULL, "%s", 0, 5, 1, 0, soundDriver, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Hudba", NULL, WHITE, &global->play_music, NULL, "%s", 0, 1, 1, 0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28}
-    };
-   MENUDESC soundMenu = { "Zvuk", 3, soundOpts, TRUE, FALSE};
-
-
-
-    static MENUENTRY graphicsOpts[12] =
-    {
-      { "Na celú obrazovku", NULL, WHITE, &global->full_screen, NULL, "%s", 0, 1, 1, 0, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 108},
-      { "Rozptyl", NULL, WHITE, &global->ditherGradients, NULL, "%s", 0, 1, 1, 1, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Detaily krajiny", NULL, WHITE, &global->detailedLandscape, NULL, "%s", 0, 1, 1, 1, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Detaily oblohy", NULL, WHITE, &global->detailedSky, NULL, "%s", 0, 1, 1, 1, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Slabnúci text", NULL, WHITE, &env->dFadingText, NULL, "%s", 0, 1, 1, 1, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Text s tieňom", NULL, WHITE, &env->dShadowedText, NULL, "%s", 0, 1, 1, 1, onOffText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Farebná téma", NULL, WHITE, &global->colour_theme, NULL, "%s", 0, 1, 1, 1, colourText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Šírka obrazovky", NULL, WHITE, &global->temp_screenWidth, NULL, "%4.0f", 800, 1600, 200, 800, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Výška obrazovky", NULL, WHITE, &global->temp_screenHeight, NULL, "%4.0f", 600, 1200, 200, 600, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Ukazovateľ myši", NULL, WHITE, &global->os_mouse, NULL, "%s", 0, 1, 1, 1, mouseText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 1, global->halfHeight + 72},
-      { "Rýchlosť hry", NULL, WHITE, &global->frames_per_second, NULL, "%3.0f", 30, 120, 5, 60, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Vlastné pozadie", NULL, WHITE, &env->custom_background, NULL, "%s", 0, 1, 1, 0, onOffText_de, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC graphicsMenu = { "Grafika", 12, graphicsOpts, TRUE, FALSE};
-
-    static MENUENTRY financeOpts[9] =
-    {
-      { "Peniaze na začiatku", NULL, WHITE, (double*)&global->startmoney, NULL, "%2.0f", 0, 200000, 5000, 20000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Úroková miera", NULL, WHITE, (double*)&global->interest, NULL, "%2.2f", 1.0, 1.5, 0.05, 1.25, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Bonus pri skončení kola", NULL, WHITE, (double*)&global->scoreRoundWinBonus, NULL, "%2.0f", 0, 50000, 5000, 10000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Odmena za poškodenie", NULL, WHITE, (double*)&global->scoreHitUnit, NULL, "%2.0f", 0, 500, 25, 75, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Pokuta za vlastné poškodenie", NULL, WHITE, (double*)&global->scoreSelfHit, NULL, "%2.0f", 0, 10000, 1000, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Bonus za zničenie tanku", NULL, WHITE, (double*)&global->scoreUnitDestroyBonus, NULL, "%2.0f", 0, 20000, 2500, 5000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Pokuta za vlastné zničenie tanku", NULL, WHITE, (double*)&global->scoreUnitSelfDestroy, NULL, "%2.0f", 0, 20000, 2500, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Násobiteľ pri predaji položiek", NULL, WHITE, (double*)&global->sellpercent, NULL, "%1.2f", 0.0, 1.0, 0.10, 0.80, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth -3, global->halfHeight + 92},
-      { "Teamy zdieľajú peniaze", NULL, WHITE, (double *) &global->divide_money, NULL, "%s", 0.0, 1.0, 1.0, 0.0, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-
-    };
-    MENUDESC financeMenu = { "Peniaze", 9, financeOpts, TRUE, FALSE};
-
-
-    static MENUENTRY networkOpts[3] =
-    {
-       {  "Kontrola aktualizácii", NULL, WHITE, (double*) &global->check_for_updates, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-       {  "Sieťová hra", NULL, WHITE, (double*) &global->enable_network, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-       {  "Port pre načúvanie", NULL, WHITE, (double*) &global->listen_port, NULL, "%5.0f", 10645, 64645, 1000, 25645, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8}
-    };
-    MENUDESC networkMenu = { "Sieť", 3, networkOpts, TRUE, FALSE};
-
-
-    void *pPhysicsMenu  =       &physicsMenu;
-    void *pWeatherMenu  =       &weatherMenu;
-    void *pGraphicsMenu =       &graphicsMenu;
-    void *pFinanceMenu  =       &financeMenu;
-    void *pnetworkMenu  =       &networkMenu;
-    void *pSoundMenu    =       &soundMenu;
-
-
-    static MENUENTRY mainOpts[12] =
-    {
-      { "Fyzika", NULL, WHITE, (double*)pPhysicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Počasie", NULL, WHITE, (double*)pWeatherMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Grafika", NULL, WHITE, (double*)pGraphicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Peniaze", NULL, WHITE, (double*)pFinanceMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Sieť", NULL, WHITE, (double*)pnetworkMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Zvuk", NULL, WHITE, (double*) pSoundMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Tech úroveň zbraní", NULL, WHITE, (double*)&env->weapontechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Tech úroveň vecí", NULL, WHITE, (double *) &env->itemtechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Krajina", NULL, WHITE, (double*)&env->landType, NULL, "%s", 0, 7, 1, LANDTYPE_HILLS, landTypeText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Poradie", NULL, WHITE, (double*)&global->turntype, NULL, "%s", 0, 3, 1, TURN_RANDOM, turnTypeText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Preskočiť hru samotného PC", NULL, WHITE, &global->skipComputerPlay, NULL, "%s", 0, 1, 1, 1, skipTypeText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112},
-      { "Jazyk", NULL, WHITE, &global->language, NULL, "%s", 0, 7, 1, 0, languageText_sk, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 132}
-    };
-    mainMenu.title = "Hlavné menu";
-    mainMenu.numEntries = 12;
-    mainMenu.entries = mainOpts;
-    mainMenu.quitButton = TRUE;
-    mainMenu.okayButton = FALSE;
-
-  }  // end of Slovak
-
-
-if (global->language == LANGUAGE_RUSSIAN)
-{
-    static MENUENTRY physicsOpts[8] =
-    {
-      { "Гравитация", NULL, WHITE, &env->gravity, NULL, "%2.3f", .025, .325, 0.025, .075, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 80},
-      { "Сила трения", NULL, WHITE, &env->viscosity, NULL, "%2.2f", .25, 2.0, 0.25, 1.0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 60},
-      { "Падение земли", NULL, WHITE, &env->landSlideType, NULL, "%s", 0, 4, 1, 3, landSlideText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 40},
-      { "Задержка падения земли", NULL, WHITE, &env->landSlideDelay, NULL, "%4.0f", 1, 5, 1, 3, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 20},
-      { "Тип стен", NULL, WHITE, &env->wallType, NULL, "%s", 0, 4, 1, 1, wallTypeText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight},
-      { "Потолок", NULL, WHITE, &env->dBoxedMode, NULL, "%s", 0, 2, 1, 0, onOffRandomText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 20},
-      { "Мощные взрывы танков", NULL, WHITE, &global->violent_death, NULL, "%s", 0, 3, 1, 0, lightningText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 40},
-      { "Задержка выстрела", NULL, WHITE, &global->max_fire_time, NULL, "%3.0f", 0, 180, 5, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 60}
-    };
-    MENUDESC physicsMenu = { "Физика", 7, physicsOpts, TRUE, FALSE};
-
-
-    static MENUENTRY weatherOpts[7] =
-    {
-      { "Метеоритный дождь", NULL, WHITE, &env->meteors, NULL, "%s", 0, 3, 1, 0, meteorText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Молнии", NULL, WHITE, &env->lightning, NULL, "%s", 0, 3, 1, 0, lightningText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Падающая грязь", NULL, WHITE, &env->falling_dirt_balls, NULL, "%s", 0, 3, 1, 0, meteorText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Удары со спутника", NULL, WHITE, &env->satellite, NULL, "%s", 0, 3, 1, 0, laserSatelliteText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Туман", NULL, WHITE, &env->fog, NULL, "%s", 0, 1, 1, 0, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Макс. сила ветра", NULL, WHITE, (double*)&env->windstrength, NULL, "%2.0f", 0, 100, 5, 40, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Изменения силы ветра", NULL, WHITE, (double*)&env->windvariation, NULL, "%2.1f", 0, 100, 3, 10, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-    };
-    MENUDESC weatherMenu = { "Погода", 7, weatherOpts, TRUE, FALSE};
-
-
-    static MENUENTRY soundOpts[3] =
-    {
-      { "All Sound", NULL, WHITE, &global->sound, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Sound Driver", NULL, WHITE, &global->sound_driver, NULL, "%s", 0, 5, 1, 0, soundDriver, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Music", NULL, WHITE, &global->play_music, NULL, "%s", 0, 1, 1, 0, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28}
-    };
-   MENUDESC soundMenu = { "Sound", 3, soundOpts, TRUE, FALSE};
-
-
-    static MENUENTRY graphicsOpts[12] =
-    {
-      { "Full Screen", NULL, WHITE, &global->full_screen, NULL, "%s", 0, 1, 1, 0, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 108},
-      { "Сглаживание", NULL, WHITE, &global->ditherGradients, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Детализированный ландшафт", NULL, WHITE, &global->detailedLandscape, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Детализированное небо", NULL, WHITE, &global->detailedSky, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Исчезающий текст", NULL, WHITE, &env->dFadingText, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Оттененный текст", NULL, WHITE, &env->dShadowedText, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Цветовая тема", NULL, WHITE, &global->colour_theme, NULL, "%s", 0, 1, 1, 1, colourText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Ширина окна игры", NULL, WHITE, &global->temp_screenWidth, NULL, "%4.0f", 800, 1600, 200, 800, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Высота окна игры", NULL, WHITE, &global->temp_screenHeight, NULL, "%4.0f", 600, 1200, 200, 600, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Курсор в игре", NULL, WHITE, &global->os_mouse, NULL, "%s", 0, 1, 1, 1, mouseText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 1, global->halfHeight + 72},
-      { "Скорость игры", NULL, WHITE, &global->frames_per_second, NULL, "%3.0f", 30, 120, 5, 60, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Собственный фон", NULL, WHITE, &env->custom_background, NULL, "%s", 0, 1, 1, 0, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC graphicsMenu = { "Графика", 12, graphicsOpts, TRUE, FALSE};
-
-
-    static MENUENTRY financeOpts[9] =
-    {
-      { "Начальные деньги", NULL, WHITE, (double*)&global->startmoney, NULL, "%2.0f", 0, 200000, 5000, 20000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Банковский процент", NULL, WHITE, (double*)&global->interest, NULL, "%2.2f", 1.0, 1.5, 0.05, 1.25, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Бонус за победу", NULL, WHITE, (double*)&global->scoreRoundWinBonus, NULL, "%2.0f", 0, 50000, 5000, 10000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Бонус за попадание", NULL, WHITE, (double*)&global->scoreHitUnit, NULL, "%2.0f", 0, 500, 25, 75, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Штраф за попадание в себя", NULL, WHITE, (double*)&global->scoreSelfHit, NULL, "%2.0f", 0, 10000, 1000, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Бонус за уничтожение", NULL, WHITE, (double*)&global->scoreUnitDestroyBonus, NULL, "%2.0f", 0, 20000, 2500, 5000, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Штраф за самоуничтожение", NULL, WHITE, (double*)&global->scoreUnitSelfDestroy, NULL, "%2.0f", 0, 20000, 2500, 0, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Коэфф. продажи снаряжения", NULL, WHITE, (double*)&global->sellpercent, NULL, "%1.2f", 0.0, 1.0, 0.10, 0.80, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth -3, global->halfHeight + 92},
-      { "Командные боеприпасы", NULL, WHITE, (double *) &global->divide_money, NULL, "%s", 0.0, 1.0, 1.0, 0.0, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112}
-    };
-    MENUDESC financeMenu = { "Экономика", 9, financeOpts, TRUE, FALSE};
-
-
-    static MENUENTRY networkOpts[3] =
-    {
-       {  "Проверять обновления", NULL, WHITE, (double*) &global->check_for_updates, NULL, "%s", 0, 1, 1, 1, onOffText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-       {  "Networking", NULL, WHITE, (double*) &global->enable_network, NULL, "%s", 0, 1, 1, 1, onOffText, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-       {  "Listen Port", NULL, WHITE, (double*) &global->listen_port, NULL, "%5.0f", 10645, 64645, 1000, 25645, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8}
-    };
-    MENUDESC networkMenu = { "Настройки сети", 3, networkOpts, TRUE, FALSE};
-
-    void *pPhysicsMenu  =       &physicsMenu;
-    void *pWeatherMenu  =       &weatherMenu;
-    void *pGraphicsMenu =       &graphicsMenu;
-    void *pFinanceMenu  =       &financeMenu;
-    void *pnetworkMenu  =       &networkMenu;
-    void *pSoundMenu    =       &soundMenu;
-
-
-    static MENUENTRY mainOpts[12] =
-    {
-      { "Физика", NULL, WHITE, (double*)pPhysicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 88},
-      { "Погода", NULL, WHITE, (double*)pWeatherMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 68},
-      { "Графика", NULL, WHITE, (double*)pGraphicsMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 48},
-      { "Экономика", NULL, WHITE, (double*)pFinanceMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 28},
-      { "Настройки сети", NULL, WHITE, (double*)pnetworkMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight - 8},
-      { "Звук", NULL, WHITE, (double*)pSoundMenu, NULL, NULL, 0, 0, 0, 0, NULL, OPTION_MENUTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 12},
-      { "Уровень оружия", NULL, WHITE, (double*)&env->weapontechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 32},
-      { "Уровень снаряжения", NULL, WHITE, (double *) &env->itemtechLevel, NULL, "%2.0f", 0, 5, 1, 5, NULL, OPTION_DOUBLETYPE, FALSE, global->halfWidth - 3, global->halfHeight + 52},
-      { "Тип ландшафта", NULL, WHITE, (double*)&env->landType, NULL, "%s", 0, 7, 1, LANDTYPE_HILLS, landTypeText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 72},
-      { "Порядок хода", NULL, WHITE, (double*)&global->turntype, NULL, "%s", 0, 3, 1, TURN_RANDOM, turnTypeText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 92},
-      { "Пропускать игру компьютеров", NULL, WHITE, &global->skipComputerPlay, NULL, "%s", 0, 1, 1, 1, skipTypeText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 112},
-      { "Язык (Language)", NULL, WHITE, &global->language, NULL, "%s", 0, 7, 1, 0, languageText_ru, OPTION_SPECIALTYPE, FALSE, global->halfWidth - 3, global->halfHeight + 132}
-    };
-// mainMenu = { "Main Menu", 10, mainOpts, TRUE, FALSE};
-     mainMenu.title = "Главное меню";
-     mainMenu.numEntries = 12;
-     mainMenu.entries = mainOpts;
-     mainMenu.quitButton = TRUE;
-     mainMenu.okayButton = FALSE;
-
-}   // end of Russian
-
-
-
-#endif
-
diff --git a/src/missile.cpp b/src/missile.cpp
index 330dc7e..670619d 100644
--- a/src/missile.cpp
+++ b/src/missile.cpp
@@ -18,782 +18,1141 @@
  * */
 
 
-#include "environment.h"
-#include "globaldata.h"
 #include "explosion.h"
 #include "missile.h"
 #include "decor.h"
 #include "tank.h"
+#include "player.h"
+#include "beam.h"
+#include "sound.h"
+#include "aicore.h"
+
+
+/* Note: If you wonder why the MISSILE ctor needs the AI_LEVEL, it is used
+ *       for two things:
+ *       1. Whether repulsion is considered for mind shots depends on the
+ *          ai_level of the bot tracking the missile, and
+ *       2. the SDI check must make sure to not re-test its own mind shots.
+ */
+MISSILE::MISSILE (PLAYER* player_, double xpos, double ypos,
+                  double xvel, double yvel, int32_t weapon_type,
+                  eMissileType missile_type, int32_t ai_level_) :
+	PHYSICAL_OBJECT(MT_WEAPON == missile_type),
+	ai_level(ai_level_),
+	missileType(missile_type)
+{
+	this->player = player_;
+
+#ifdef NETWORK
+	char buffer[256];
+	sprintf(buffer, "MISSILE %d %d %lf %lf %d",
+			ROUND(xpos), ROUND(ypos),
+			xvel, yvel, weapon_type);
+	env.sendToClients(buffer);
+#endif
+
+	// Set position and movement
+	x  = xpos;
+	y  = ypos;
+	xv = xvel;
+	yv = yvel;
+
+	// Get and set weapon/item data
+	weapType = weapon_type;
+	if (weapType < WEAPONS)
+		weap = &weapon[weapType];
+	else
+		weap = &naturals[weapType - WEAPONS];
+	setBitmap(env.missile[weap->picpoint]);
+	drag       = weap->drag;
+	mass       = weap->mass;
+	noimpact   = weap->noimpact;
+
+	// The maxVel value results in a small missile being able to be accelerated
+	// by 25% over MAX_POWER, while a large Napalm Bomb can go up to 220%.
+	maxVel = env.maxVelocity * (1.20 + (mass / (.01 * MAX_POWER)));
+	DEBUG_LOG_PHY("PHYSICAL_OBJECT",
+	              "env.maxVel: %5.2lf, mass: %5.2lf, obj.maxVel: %5.2lf",
+	              env.maxVelocity, mass, maxVel)
+
+	// Meteors and dirt balls are "volatile" and can not be accelerated
+	// over MAX_POWER. (Pre-caution against "forever" going naturals)
+	if ( ( (SML_METEOR <= weapType) && (LRG_METEOR    >= weapType) )
+	  || ( (DIRT_BALL  <= weapType) && (SUP_DIRT_BALL >= weapType) ) )
+		maxVel = std::min(maxVel, static_cast<double>(MAX_POWER));
+
+	if ( (SML_METEOR <= weapType) && (LRG_METEOR >= weapType) ) {
+		angle  =  rand () % 360;
+		spin   = (rand () %  20) - 10;
+		maxAge = MAX_METEOR_AGE;
+	} else if (weapType == NAPALM_JELLY) {
+		// Napalm grows, others do not:
+		isGrowing      = true;
+		growRadius     = 1;
+		maxAge         = MAX_JELLY_AGE;
+		allowDirtyWrap = false;
+	} else {
+		growRadius = weap->radius;
+		if (FUNKY_BOMBLET == weapType)
+			maxAge = (MAX_MISSILE_AGE / 7) + (rand() % (MAX_MISSILE_AGE / 4));
+			// With MMA 15 seconds, this is 2 + [0;3] = [2;5] seconds
+		else if (FUNKY_DEATHLET == weapType)
+			maxAge = (MAX_MISSILE_AGE / 5) + (rand() % (MAX_MISSILE_AGE / 3));
+			// With MMA 15 seconds, this is 3 + [0;4] = [3;7] seconds
+		else
+			maxAge = MAX_MISSILE_AGE;
+	}
+
+	// Finalize maxAge to be for frames, not seconds:
+	maxAge *= env.frames_per_second;
+
+	// Set funky colour of the funky bomblets/deathlets and add some maxAge
+	// variation so they do not detonate in groups.
+	if ( (weapType == FUNKY_BOMBLET) || (weapType == FUNKY_DEATHLET) ) {
+		int32_t temp_number = rand() % 5;
+		switch (temp_number)
+		{
+			case 0: funky_colour = makecol(200,   0,   0); break;
+			case 1: funky_colour = makecol(  0, 200,   0); break;
+			case 2: funky_colour = makecol(  0,   0, 200); break;
+			case 3: funky_colour = makecol(200, 200,   0); break;
+			case 4: funky_colour = makecol(200,   0, 200); break;
+		}
+
+		// Variation +/- 1 Second in frames:
+		maxAge += (rand() % (2 * env.frames_per_second)) - env.frames_per_second;
+	}
+
+	// Some weapons must not wrap through dirt ceilings if the bottom
+	// pixel they would warp into is occupied:
+	if ( ( (SML_ROLLER <= weapType) && (SMALL_MIRV  >= weapType) )
+	  || ( (CLUSTER    <= weapType) && (SUP_CLUSTER >= weapType) )
+	  || ( (SML_NAPALM <= weapType) && (LRG_NAPALM  >= weapType) )
+	  || (CLUSTER_MIRV == weapType) )
+		allowDirtyWrap = false;
+
+	// Add to the chain:
+	// Do not put mind shots in there or the object updaters
+	// will not only try to apply physics, but use delete on
+	// them when they get destroyed.
+	if (MT_MIND_SHOT != missileType)
+		global.addObject(this);
+}
 
 
 MISSILE::~MISSILE ()
 {
-  _env->removeObject (this);
-  _global = NULL;
-  _env    = NULL;
-  weap    = NULL;
+	// Take out of the chain:
+	if (MT_MIND_SHOT != missileType)
+		global.removeObject(this);
 }
 
-MISSILE::MISSILE (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, 
-                  double xvel, double yvel,
-                  int weaponType):PHYSICAL_OBJECT(),weap(NULL)
-{
-  setEnvironment (env);
-  player = NULL;
-  _align = LEFT;
-  _global = global;
-  #ifdef NETWORK
-  char buffer[256];
-  sprintf(buffer, "MISSILE %d %d %lf %lf %d", (int)xpos, (int)ypos,
-                  xvel, yvel, weaponType);
-  global->Send_To_Clients(buffer);
-  #endif
-  x = xpos;
-  y = ypos;
-  xv = xvel;
-  yv = yvel;
-  type = weaponType;
-  if (type < WEAPONS)
-    weap = &weapon[type];
-  else
-    weap = &naturals[type - WEAPONS];
-  mass = weap->mass;
-  drag = weap->drag;
-  sound = weap->sound;
-  expSize = weap->radius;
-  etime = weap->etime;
-  damage = weap->damage;
-  eframes = weap->eframes;
-  picpoint = weap->picpoint;
-  noimpact = weap->noimpact;
-  countdown = -1;
-  _bitmap = (BITMAP *)_global->missile[picpoint];
-  physics = 0;
-  age = 0;
-  destroy = FALSE;
-  if (type >= SML_METEOR && type <= LRG_METEOR)
-    {
-      angle = rand () % 360;
-      spin = rand () % 20 - 10;
-    }
-  else
-    {
-      angle = 0;
-      spin = 0;
-    }
-
-  // Napalm grows, others do not:
-  if (type == NAPALM_JELLY)
-    {
-     bIsGrowing = true;
-     iGrowRadius= 1;
-    }
-  else
-    {
-     bIsGrowing = false;
-     iGrowRadius= expSize;
-    }
-  if ( (type == FUNKY_BOMBLET) || (type == FUNKY_DEATHLET) )
-  {
-      int temp_number = rand() % 5;
-      switch (temp_number)
-      {
-         case 0: funky_colour = makecol(200, 0, 0); break;
-         case 1: funky_colour = makecol(0, 200, 0); break;
-         case 2: funky_colour = makecol(0, 0, 200); break;
-         case 3: funky_colour = makecol(200, 200, 0); break;
-         case 4: funky_colour = makecol(200, 0, 200); break;
-      }
-  }
-}
 
-void MISSILE::initialise ()
+void MISSILE::applyPhysics ()
 {
-  PHYSICAL_OBJECT::initialise ();
-  physics = 0;
-  age = 0;
-  if (type >= SML_METEOR && type <= LRG_METEOR)
-    {
-      angle = rand () % 360;
-      spin = rand () % 20 - 10;
-    }
-  else
-    {
-      angle = 0;
-      spin = 0;
-    }
+	int32_t above_ground = -1;
+
+	// Increase age and get rid of the missile if it
+	// is caught in some endless loop.
+	if ( (++age > maxAge)
+	  || (y < -65535) ) {
+		hitSomething = true;
+		if (MT_MIND_SHOT != missileType)
+			trigger();
+		else
+			destroy = true;
+		return;
+	} else
+		hitSomething = false;
+
+	// Napalm grows first:
+	if (isGrowing) {
+		if (age < maxAge) {
+			growRadius = ROUND(static_cast<double>(weap->radius)
+								* (static_cast<double>(age)
+										/ static_cast<double>(maxAge)));
+			if (growRadius < 2)
+				growRadius = 2;
+		} else
+			isGrowing = false; // Finished growing!
+	}
+
+	// Riot charges / blasts go off immediately:
+	if ( (RIOT_CHARGE <= weapType) && (RIOT_BLAST >= weapType) ) {
+		trigger();
+		return;
+	}
+
+
+	// Normal and digging physic types need an angle
+	// and can be repulsed
+	if ( (PT_NORMAL == physType) || (PT_DIGGING == physType)) {
+
+		if ( (SML_METEOR <= weapType) && (LRG_METEOR >= weapType) )
+			angle = (angle + spin) % 360;
+		else
+			angle = ROUND(RAD2DEG(atan(yv / xv)) * 256./360.)
+			      - 64 + ( xv < 0 ? 128 : 0);
+
+		if ( (MT_MIND_SHOT != missileType)
+		  || (RAND_AI_0P && RAND_AI_0P) )
+			Repulse_Missile();
+	}
+
+	// === 1) Handle standard physics projectiles ===
+	if (PT_NORMAL == physType) {
+		// Standard physics can be applied
+		PHYSICAL_OBJECT::applyPhysics ();
+
+
+		// mirvs trigger above ground
+		if ( !hitSomething
+		  && ( (weapType == SMALL_MIRV) || (weapType == CLUSTER_MIRV) )
+		  && (yv > 0) ) {
+
+			above_ground = Height_Above_Ground();
+
+			if ( (above_ground > 0)
+			  && (above_ground < TRIGGER_HEIGHT) )
+				hitSomething = true;
+		}
+
+
+		// Missiles that get too slow on a rubber floor, trigger when stopped.
+		if ( !hitSomething && (WALL_RUBBER == env.current_wallType)
+		  && (ROUND(y) >= (env.screenHeight - 2))
+		  && ( (std::abs(xv) + std::abs(yv)) < 0.8) )
+			hitSomething = true;
+
+
+		// Unless something is hit, smoke might be produced:
+		if ( !hitSomething && !global.skippingComputerPlay
+		  && (MT_MIND_SHOT != missileType)
+		  && !(rand() % (env.frames_per_second / 10)) ) {
+			try {
+				new DECOR (x, y,
+							xv / env.frames_per_second,
+							xv / env.frames_per_second,
+							weap->radius / 20, DECOR_SMOKE, 0);
+			} catch (std::exception) {
+				perror ( "missile.cpp: Failed allocating memory for decor in applyPhysics");
+			}
+		}
+	} // --- End of normal physic types ---
+
+	// === 2) Handle rolling projectiles ===
+	else if (PT_ROLLING == physType) {
+		// check whether anything is hit
+		if ( (x < 2)
+		  || (x > (env.screenWidth  - 3) )
+		  || (y > (env.screenHeight - 3) ) ) {
+			int32_t round_x = ROUND(x);
+			int32_t round_y = ROUND(y);
+			if(PINK != getpixel(global.terrain, round_x, round_y))
+				hitSomething = true;
+		}
+
+		if (!hitSomething) {
+			// roll roller
+			float surfY = global.surface[ROUND(x)].load(ATOMIC_READ) - 1;
+
+			// Check whether terrain is going down
+			if ( (surfY > y) && (y < (env.screenHeight - 5)) ) {
+
+				// Do not fall more than five pixels if terrain gives way.
+				if ( surfY < (y + 5) )
+					y  = surfY;
+				else
+					y += 5;
+			} else {
+				// Normal movement:
+				x += xv > 0 ? 1 : xv < 0 ? -1 : 0;
+
+				if	(y >= MENUHEIGHT) {
+					// The small roller can climb two, the large three and
+					// the death roller four pixels. Everything else
+					// rolling is limited to one pixel.
+					int32_t maxHeight = y -
+								(SML_ROLLER == weapType ? 2 :
+								 LRG_ROLLER == weapType ? 3 :
+								 DTH_ROLLER == weapType ? 4 : 1);
+					if (surfY >= maxHeight)
+						y = surfY - 1;
+				}
+			} // End of normal movement
+
+			// Adapt angle according to movement
+			if (xv > 0.0) angle = (angle +   3) % 256;
+			if (xv < 0.0) angle = (angle + 253) % 256;
+
+			// Fix y if the projectile threats to go through the floor
+			if (!hitSomething && (y > (env.screenHeight - 5)) )
+				y = env.screenHeight - 5;
+		} // End of rolling projectile movement
+	} // --- End of rolling physic types ---
+
+	// === 3) Handle funky projectiles ===
+	else if (PT_FUNKY_FLOAT == physType)  {
+
+		// Funky Floats have a 0.5% chance to randomly reverse either direction:
+		if (0 == (rand() % 200)) {
+			if (rand() % 2)
+				xv *= -1.;
+			else
+				yv *= -1.;
+		}
+
+		// Funky floats simply bounce on borders.
+		if ( ((x + xv) < 1) || ((x + xv) > (env.screenWidth - 1) ) )
+			xv = -xv;
+		else
+			x += xv;
+
+		// The same applies to the screen bottom,
+		// but here according to floor type
+		if ((y + yv) >= env.screenHeight) {
+			if (WALL_RUBBER == env.current_wallType) {
+				yv *= -BOUNCE_CHANGE;
+				xv *=  0.95;
+			} else if (WALL_SPRING == env.current_wallType) {
+				yv *= -SPRING_CHANGE;
+				xv *= 1.05;
+			} else if ( (WALL_WRAP == env.current_wallType)
+			         && env.isBoxed && env.do_box_wrap ) {
+				y  = MENUHEIGHT + 1;
+			} else {
+				y  = env.screenHeight;
+				yv = 0;
+				hitSomething = true;
+				age = maxAge;
+			}
+		}
+
+		// On the screen top the direction is just reversed, even if
+		// there is a ceiling in boxed mode. However, if it is a wrap
+		// ceiling, and the ceiling wrap is enabled, got to the bottom.
+		else if ( (y + yv) <= MENUHEIGHT) {
+			if ( (WALL_WRAP == env.current_wallType)
+			  && env.isBoxed && env.do_box_wrap ) {
+				y  = env.screenHeight - 2;
+			} else {
+				yv *= -0.95;
+				xv *=  0.95;
+			}
+		}
+
+		// eventually apply yv
+		y += yv;
+	} // --- End of funky float physic types ---
+
+	// === 4) Handle other projectiles ===
+	else {
+
+		// Check X:
+		if ( ((x + xv) < 1) || ((x + xv) > (env.screenWidth - 1)) ) {
+			if (WALL_RUBBER == env.current_wallType)
+				xv *= -0.5;
+			else if (WALL_SPRING == env.current_wallType)
+				xv *= -SPRING_CHANGE;
+			else if (WALL_WRAP == env.current_wallType)
+				x  = xv > 0. ? 1 : env.screenWidth - 1;
+			else {
+				x  = xv < 0. ? 1 : env.screenWidth - 1;
+				xv = 0;
+				hitSomething = true;
+			}
+		}
+
+		// Check Y :
+		if ( ((y + yv) >= env.screenHeight) || ((y + yv) < MENUHEIGHT) ) {
+			yv *= -0.5;
+			xv *=  0.95;
+		}
+
+		// Apply gravitation, digging types are reversed and scorch the ground:
+		if (PT_DIGGING == physType)
+			yv -= env.gravity * 0.05 * env.FPS_mod;
+		else
+			yv += env.gravity * 0.05 * env.FPS_mod;
+
+		// Apply velocity
+		y  += yv;
+		x  += xv;
+
+	} // End of checking 'others'
+
+	// Final check against the terrain
+	if (!hitSomething && (y > MENUHEIGHT)
+	  && (y < (env.screenHeight - 1)) ) {
+		int32_t round_x = ROUND(x);
+		int32_t round_y = ROUND(y);
+		int32_t hitpix = getpixel(global.terrain, round_x, round_y);
+		if ( ( (PT_DIGGING == physType) && (PINK == hitpix) )
+		  || ( (PT_DIGGING != physType) && (PINK != hitpix) ) )
+			hitSomething = true;
+	}
+
+	// No "ceiling drops" are triggered in boxed mode
+	if (!hitSomething && (y <= MENUHEIGHT) && env.isBoxed) {
+		yv = 0;
+		hitSomething = true;
+	}
+
+	// Be sure any trigger/detonation is on screen ...
+	if (hitSomething && (y <= MENUHEIGHT))
+		y = MENUHEIGHT + 1;
+
+	triggerTest();
 }
 
 
-
-int MISSILE::triggerTest ()
+/// @return The number of bounces done since the missile was fired
+int32_t MISSILE::bounced() const
 {
-  int quell = 1;
-  TANK *tank;
-  double old_delta_x = xv;
-
-  // Has it hit a tank?
-  for (int objCount = 0; (tank = (TANK*)_env->getNextOfClass (TANK_CLASS, &objCount)) && tank; objCount++)
-    {
-      if (x > tank->x - TANKWIDTH && x < tank->x + TANKWIDTH && y > tank->y && y < tank->y + TANKHEIGHT && tank->l > 0)
-        {
-          hitSomething = 1;
-          tank->requireUpdate ();
-        }
-    }
-  if (noimpact)
-    {
-      quell = 1;
-    }
-  else if ((y > 0) && (hitSomething || (y >= _global->screenHeight) || (getpixel (_env->terrain, (int)x, (int)y) != PINK)))
-    // else if ( (y > 0) && (hitSomething || (y >= _global->screenHeight) || (_env->surface[(int)x] <= y) ) )
-    {
-      quell = 0;
-      if (type >= SML_ROLLER && type <= DTH_ROLLER && !physics)
-        {
-          /* rollerupdate */
-          if (age > 1)
-            {
-
-              quell = 1;
-              physics = 1;
-              y -= 5;
-              if (x < 1)
-                x = 1;
-              if (x > (_global->screenWidth - 2))
-                x = (_global->screenWidth - 2);
-
-              if ( (y >= _env->surface[(int)x])       // y is surface or below
-                   && (y <= _env->surface[(int)x] + 2) )  // but not burried more than 2 px
-                y = _env->surface[(int)x] - 1;
-
-              yv = 0.0;
-              // if (xv > 0.0) yv = 1.0;
-              // if (xv < 0.0) yv =-1.0;
-
-              xv = 0.0;
-              if (x == 1)
-                  xv++;
-              else if (x == (_global->screenWidth - 2) )
-                  xv--;
-              else
-              {
-                  int can_go_left = FALSE, can_go_right = FALSE;
-                  if (getpixel (_env->terrain, (int)x + 1, (int)y + 1) == PINK)
-                     can_go_right = TRUE;
-                  if (getpixel (_env->terrain, (int)x - 1, (int)y + 1) == PINK)
-                     can_go_left = TRUE;
-
-                  if ( (can_go_left) && (!can_go_right) )
-                  {
-                     xv--;
-                  }
-                  else if ( (! can_go_left) && (can_go_right) )
-                  {
-                     xv++;
-                  }
-                  else if ( (can_go_left) && (can_go_right) )
-                  {
-                     xv = old_delta_x;      // if both are open, continue on old course
-                  }
-              }
-              if (xv == 0.0)     // nothing worked, try something!
-              {
-                xv = yv;
-              }
-              yv = 0.0;
-              hitSomething = 0;
-            }
-          else
-            {
-              physics = 4;
-            }
-        }
-      else if (type == BURROWER || type == PENETRATOR)
-        {
-          if (physics == 1)
-            {
-              if (!hitSomething)
-                quell = 1;
-            }
-          if (!physics)
-            {
-              physics = 1;
-              xv *= 0.1;
-              yv *= 0.1;
-              quell = 1;
-            }
-
-        }
-      // our weapon has sub weapons
-      else if (weap->submunition >= 0)
-        {
-          WEAPON *submunition = &weapon[weap->submunition];
-
-          quell = 1;
-          if (weap->numSubmunitions > 0)
-            {
-              double divergenceStep = weap->divergence / (weap->numSubmunitions - 1);
-              int startPoint;
-              int randStart = rand () % 1000000;
-              int submunitionPhys = 0;
-              double inheritedXV = weap->impartVelocity * xv;
-              double inheritedYV = weap->impartVelocity * yv;
-
-              if (type == FUNKY_BOMB || type == FUNKY_DEATH)
-                submunitionPhys = 1;
-
-              if (divergenceStep < 0)
-                {
-                  startPoint = 0;
-                }
-              else
-                {
-                  startPoint = 180;
-                }
-              for (int spreadCount = 0; spreadCount < weap->numSubmunitions; spreadCount++)
-                {
-                  MISSILE *newmis;
-                  double launchSpeed;
-                  int newmisCount;
-                  int dAngle;
-
-                  dAngle = (int) (startPoint - (weap->divergence / 2) + (divergenceStep * spreadCount));
-                  if (weap->spreadVariation > 0)
-                    {
-                      double variation = Noise (randStart + 1054 + spreadCount) * weap->spreadVariation;
-                      dAngle += (int)(weap->divergence * variation);
-                    }
-                  while (dAngle < 0)
-                    dAngle += 360;
-                  while (dAngle >= 360)
-                    dAngle -= 360;
-
-                  newmisCount = submunition->countdown;
-                  if (submunition->countVariation > 0)
-                    {
-                      double variation = Noise (randStart + 78689 + spreadCount) * submunition->countVariation;
-                      newmisCount += (int)(submunition->countdown * variation);
-                      if (newmisCount <= 0)
-                        newmisCount = 1;
-                    }
-
-                  launchSpeed = weap->launchSpeed;
-                  if (weap->speedVariation > 0)
-                    {
-                      double variation = Noise (randStart + 124786 + spreadCount) * weap->speedVariation;
-                      launchSpeed += weap->launchSpeed * variation;
-                    }
-
-                  newmis = new MISSILE (_global, _env, x, y - 20, _global->slope[dAngle][0] * launchSpeed * (100.0 / _global->frames_per_second) + inheritedXV, _global->slope[dAngle][1] * launchSpeed * (100.0 / _global->frames_per_second) + inheritedYV, weap->submunition);
-
-                  if (newmis)
-                  {
-
-                      newmis->physics = submunitionPhys;
-                      newmis->player = player;
-                      newmis->countdown = newmisCount;
-                      newmis->noimpact = weapon[weap->submunition].noimpact;
-                      newmis->setUpdateArea
-                       ((int) newmis->x - 20,
-                       (int) newmis->y - 20, 40, 40);
-                  }
-                  else
-                      perror ( "missile.cc: Failed allocating memory for newmis in MISSILE::triggerTest (CLUSTER)");
-                }
-            }
-          destroy = TRUE;
-        }
-    }
-
-  if (countdown >= 0 && age >= countdown)
-    {
-      quell = 0;
-    }
-  if (type >= RIOT_CHARGE && type <= RIOT_BLAST)
-    {
-      quell = 0;
-      destroy = TRUE;
-    }
-  if (!quell)
-    {
-      _env->realm--;
-      trigger ();
-    }
-
-
-  return (!quell);
+	return bounces;
 }
 
-int MISSILE::applyPhysics ()
+
+/// @return -1 if the missile flies to the left, 1 if it flies to the right
+/// or does not have any vertical movement
+int32_t MISSILE::direction() const
 {
-  TANK *ltank;
-  int detonate = FALSE;
-  int max_age;
-  int above_ground;
-
-  age++;
-
-  // meteors live less time than regular missles
-  if ( (type >= SML_METEOR) && (type <= LRG_METEOR) )
-    max_age = MAX_METEOR_AGE;
-  else
-    max_age = MAX_MISSLE_AGE;
-
-  // Napalm grows first:
-  if (bIsGrowing)
-    {
-      if (age < max_age)
-        {
-          iGrowRadius = (int)round((double)expSize * ((double)age / (double)max_age));
-          if (iGrowRadius < 2)
-            iGrowRadius = 2;
-        }
-      else
-        bIsGrowing = false; // Finished growing!
-    }
-
-  if ( (age > max_age * _global->frames_per_second) || (y < -65535) )
-    detonate = 1;
-  if (!physics)
-    {
-      if (type >= SML_METEOR && type <= LRG_METEOR)
-        {
-          angle += spin;
-          angle = angle % 360;
-        }
-      else
-        {
-          angle = (int) (atan (yv / xv) * (180 / PI) * ACHANGE) - 64 + ((xv < 0) ? 128 : 0);
-        }
-      for (int objCount = 0; (ltank = (TANK*)_env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-        {
-          if (ltank->player != player)
-            {
-              double xaccel = 0, yaccel = 0;
-              ltank->repulse (x + xv, y + yv, &xaccel, &yaccel, type);
-              xv += xaccel;
-              yv += yaccel;
-            }
-        }
-      if ( PHYSICAL_OBJECT::applyPhysics () )
-      {
-        detonate = 1;
-      }
-      if (! detonate)
-        detonate = checkPixelsBetweenPrevAndNow ();
-
-      // mirvs trigger above ground
-      if ( (! detonate) && ( (type == SMALL_MIRV) || (type == CLUSTER_MIRV) ) )
-        {
-          above_ground = Height_Above_Ground();
-          if ( (above_ground > 0) && (above_ground < TIGGER_HEIGHT) &&
-               (yv > 0) )
-            detonate = 1;
-        }
-
-      // if (! detonate &&
-      if ( (type == BURROWER || type == PENETRATOR) )
-        {
-          hitSomething = 0;
-          if ( ( y >= _global->screenHeight) &&
-               ( (_env->current_wallType == WALL_STEEL) || (_env->current_wallType == WALL_WRAP) ) )
-            {
-              detonate = TRUE;
-              hitSomething = 1;
-              y = _global->screenHeight;
-              yv = 0;
-            }
-        }
-      if (rand () % 5 == 0)
-        {
-          DECOR *decor;
-          decor = new DECOR (_global, _env, x, y, xv, yv, expSize / 20, DECOR_SMOKE);
-          if (!decor)
-            {
-              perror ( "missile.cc: Failed allocating memory for decor in MISSILE::applyPhysics (METEOR)");
-              // exit (1);
-            }
-        }
-    }
-  else
-    {
-      if (type >= SML_ROLLER && type <= DTH_ROLLER)
-        {
-          /* rollerupdate */
-          // check whether we have hit anything
-          if (	 (x < 1)
-                ||(x > (_global->screenWidth - 2))
-                ||(y > (_global->screenHeight - 2))
-                ||(getpixel(_env->terrain, (int)x, (int)y) != PINK))
-            detonate = 1;
-          else
-            {
-              // roll roller
-              float fSurfY = (float)_env->surface[(int)x] - 1;
-              if ((fSurfY > y) && (y < (_global->screenHeight - 5)))
-                {
-                  if (fSurfY < (y + 5))
-                    y = fSurfY;
-                  else
-                    y+=5;
-                  if (xv > 0.0) 		angle += 3;
-                  if (xv < 0.0) 		angle -= 3;
-                  if (angle < 0)		angle += 256;
-                  if (angle > 255)	angle -= 256;
-                }
-              else
-                {
-                  if (xv > 0.0)
-                    {
-                      angle += 3;
-                      x++;
-                    }
-                  else
-                    {
-                      angle -= 3;
-                      x--;
-                    }
-                  if (angle < 0)
-                    angle += 256;
-                  if (angle > 255)
-                    angle -= 256;
-                  if	(y >= MENUHEIGHT)
-                    {
-                      if (fSurfY > y)
-                        y++;
-                      else if (fSurfY >= (y - 2))
-                        y = fSurfY - 1;
-                    }
-                }
-              if (y > (_global->screenHeight - 5) && !detonate)
-                y = (_global->screenHeight - 5);
-            }
-        }
-      else if (type == FUNKY_BOMBLET || type == FUNKY_DEATHLET)
-        {
-          //double accel = (_env->wind - xv) / mass * drag;
-          if (x + xv < 1 || x + xv > (_global->screenWidth-1))
-            {
-              xv = -xv;	//bounce on the border
-            }
-          else
-            {
-              x += xv;
-            }
-
-          // hit screen bottom then change the y velocity direction
-          if (y + yv >= _global->screenHeight)
-            {
-              // only bounce if the wall is rubber
-              if ( _env->current_wallType == WALL_RUBBER )
-                {
-                  yv = -yv * 0.5;
-                  xv *= 0.95;
-                }
-              // bounce back with more force
-              else if ( _env->current_wallType == WALL_SPRING )
-                {
-                  yv = -yv * SPRING_CHANGE;
-                  xv *= 1.05;
-                }
-              // steel or wrap floor, detonate
-              else
-                {
-                  y = _global->screenHeight;
-                  yv = 0;
-                  detonate = TRUE;
-                }
-            }
-          else if (y+yv < 0) //hit screen top
-            {
-              yv = -yv * 0.5;
-              xv *= 0.95;
-            }
-          y += yv;
-        }
-      else if (type == BURROWER || type == PENETRATOR)
-        {
-          angle = (int) (atan (yv / xv) * (180 / PI) * ACHANGE) - 64 + ((xv < 0) ? 128 : 0);
-          for (int objCount = 0; (ltank = (TANK*)_env->getNextOfClass (TANK_CLASS, &objCount)) && ltank; objCount++)
-            {
-              if (ltank->player != player)
-                {
-                  double xaccel = 0, yaccel = 0;
-                  ltank->repulse (x + xv, y + yv, &xaccel, &yaccel, type);
-                  xv += xaccel * 0.1;
-                  yv += yaccel * 0.1;
-                }
-            }
-          if (x + xv < 1 || x + xv > (_global->screenWidth-1))
-            {
-              // if the wall is rubber, then bouce
-              if ( _env->current_wallType == WALL_RUBBER )
-                xv = -xv;	//bounce on the border
-              // bounce with more force
-              else if ( _env->current_wallType == WALL_SPRING )
-                xv = -xv * SPRING_CHANGE;
-              // wall is steel, detonate
-              else if ( _env->current_wallType == WALL_STEEL )
-                detonate = TRUE;
-              // wrap around to other side of the screen
-              else if ( _env->current_wallType == WALL_WRAP )
-                {
-                  if (xv < 0)
-                    x = _global->screenWidth - 1;
-                  else
-                    x = 1;
-                }
-            }
-          if (y + yv >= _global->screenHeight)
-            {
-              yv = -yv * 0.5;
-              xv *= 0.95;
-            }
-          else if (y+yv < 0)   //hit screen top
-            {
-              yv = -yv *0.5;
-              xv *= 0.95;
-            }
-          y += yv;
-          x += xv;
-          yv -= _env->gravity * 0.05 * (100.0 / _global->frames_per_second);
-          if (getpixel (_env->terrain, (int)x, (int)y) == PINK)
-            {
-              detonate = TRUE;
-            }
-        }
-    }
-
-  if (detonate)
-    {
-      hitSomething = 1;
-      if (y <= MENUHEIGHT)
-        y = MENUHEIGHT + 1;
-    }
-  else if ((y <= MENUHEIGHT) && _global->bIsBoxed)
-    {
-      detonate = 1;
-      hitSomething = 1; // Sorry, no more ceiling-drops for rollers!
-    }
-
-  return (detonate);
+	return SIGN(xv);
 }
 
 
-void MISSILE::trigger ()
+void MISSILE::draw ()
 {
-  EXPLOSION *explosion;
-
-  if (type >= TREMOR && type <= TECTONIC)
-    explosion = new EXPLOSION (_global, _env, x, y, xv, yv, type);
-  else if (type >= RIOT_CHARGE && type <= RIOT_BLAST) 
-    explosion = new EXPLOSION (_global, _env, x, y, xv, yv, type);
-  else
-    explosion = new EXPLOSION (_global, _env, x, y, type);
-
-  if (!explosion)
-    {
-      perror ( "missile.cc: Failed allocating memory for explosion in MISSILE::trigger");
-      // exit (1);
-    }
-  else
-     explosion->player = player;
-
-
-
-  if ((_env->current_wallType == WALL_WRAP) && (type < WEAPONS))
-    {
-      EXPLOSION *cSecondExplo = NULL;
-      if (x < weapon[type].radius)
-        {
-          if   (   (type >= TREMOR && type <= TECTONIC)
-                   ||(type >= RIOT_CHARGE && type <= RIOT_BLAST)   )
-            cSecondExplo = new EXPLOSION (_global, _env, _global->screenWidth + x, y, xv, yv, type);
-          else
-            cSecondExplo = new EXPLOSION (_global, _env, _global->screenWidth + x, y, type);
-          if (!cSecondExplo)
-            {
-              perror ( "missile.cc: Failed allocating memory for cSecondExplo in MISSILE::trigger");
-              // exit (1);
-            }
-        }
-      if (x > (_global->screenWidth - weapon[type].radius))
-        {
-          if   (   (type >= TREMOR && type <= TECTONIC)
-                   ||(type >= RIOT_CHARGE && type <= RIOT_BLAST)   )
-            cSecondExplo = new EXPLOSION (_global, _env, x - _global->screenWidth, y, xv, yv, type);
-          else
-            cSecondExplo = new EXPLOSION (_global, _env, x - _global->screenWidth, y, type);
-          if (!cSecondExplo)
-            {
-              perror ( "missile.cc: Failed allocating memory for cSecondExplo in MISSILE::trigger");
-              // exit (1);
-            }
-        }
-      if (cSecondExplo)
-        cSecondExplo->player = player;
-    }
-
-
-
-  destroy = TRUE;
-
-  if ((type >= DIRT_BALL && type <= SMALL_DIRT_SPREAD) || (type >= RIOT_CHARGE && type <= RIOT_BLAST))
-    {
-      play_sample ((SAMPLE *) _global->sounds[9], _env->scaleVolume(128 + (expSize / 2)), 128, 1000 - (expSize * 2), 0);
-    }
-  else if (type == NAPALM_JELLY)
-    {
-      // TODO?
-      //play_sample ((SAMPLE *) _global->SOUND[9].dat, 128 + (expSize / 2), 128, 1000 - (expSize * 2), 0);
-    }
-  else
-    {
-      play_sample ((SAMPLE *) _global->sounds[WEAPONSOUNDS + sound], _env->scaleVolume(255), 128, 1000, 0);
-    }
+	if (destroy)
+		return;
+
+	// Do not draw mind shots
+	if (MT_MIND_SHOT == missileType)
+		return;
+
+	// draw arrow impostor if it is above the screen
+	if (y < MENUHEIGHT) {
+		// Back up original values
+		BITMAP* bbitmap = getBitmap();
+		double  by      = y;
+		int32_t bangle  = angle;
+
+		// Set arrow values
+		setBitmap(env.misc[3]);
+		y      = MENUHEIGHT + (height / 2);
+		angle  = 0;
+		VIRTUAL_OBJECT::draw();
+
+		// restore original values:
+		setBitmap(bbitmap);
+		y      = by;
+		angle  = bangle;
+
+		return;
+	}
+
+	// draw missile on the screen
+
+	// Napalm jellies need a special drawing due to their growing nature.
+	if (weapType == NAPALM_JELLY) {
+		if (isGrowing)
+			draw_Napalm_Blob(this, x, y, growRadius, age / weap->etime);
+		else
+			draw_Napalm_Blob(this, x, y, weap->radius, age / weap->etime);
+
+	}   // end of napalm
+
+	// try drawing a funky bomblet
+	else if ( (FUNKY_BOMBLET  == weapType)
+		   || (FUNKY_DEATHLET == weapType) ) {
+
+		circlefill(global.canvas, x, y, 4, funky_colour );
+		circle    (global.canvas, x, y, 5, BLACK );
+		setUpdateArea( x - 10, y - 10, 20, 20);
+		requireUpdate();
+	}
+
+	// draw anything else
+	else {
+
+		// Digging weapons scorch the earth they travel through
+		if (PT_DIGGING == physType) {
+			int32_t scorches = 3 + (3 * ABSDISTANCE2(x, y, x + xv, y + yv));
+
+			for (int32_t i = 0; i < scorches; ++i) {
+				int32_t sx = x + ((rand() % 5) - 2); // [-2;2]
+				int32_t sy = y + ((rand() % 5) - 2); // [-2;2]
+
+				if ( (sx > 1)          && (sx < env.screenWidth)
+				  && (sy > MENUHEIGHT) && (sy < env.screenHeight) ) {
+					int32_t pc = getpixel(global.terrain, sx, sy);
+					if (PINK != pc) {
+						putpixel(global.terrain, sx, sy, makecol(
+							ROUNDu(static_cast<double>(getr(pc)) * .900),
+							ROUNDu(static_cast<double>(getg(pc)) * .825),
+							ROUNDu(static_cast<double>(getb(pc)) * .866)));
+					}
+				} // end of having valid coordinates
+			} // end of looping scorches
+		} // end of scorching
+
+		VIRTUAL_OBJECT::draw();
+	}
 }
 
-void MISSILE::draw (BITMAP *dest)
-{
-  if (!destroy)
-    {
-      // draw missile if it is above the screen
-      if (y < MENUHEIGHT)
-        {
-          BITMAP *bbitmap = _bitmap;
-          double by = y;
-          int bangle = angle;
-
-          _bitmap = (BITMAP*)_global->misc[3];
-          y = (double)MENUHEIGHT + (_bitmap->h / 2);
-          angle = 0;
-
-          VIRTUAL_OBJECT::draw (dest);
-          _bitmap = bbitmap;
-          y = by;
-          angle = bangle;
-        }
-
-      // draw missile on the screen
-      else
-        {
-
-          if (type == NAPALM_JELLY)
-            {
-              if (bIsGrowing)
-                {
-                  circlefill (_env->db, (int)x, (int)y, iGrowRadius - 2, makecol (255, 0, 0));
-                  circle(_env->db, (int)x, (int)y, iGrowRadius - 1, makecol(255, 150, 0));
-                  circle(_env->db, (int)x, (int)y, iGrowRadius, makecol(255, 150, 0));
-                  setUpdateArea ( (int)x - (iGrowRadius + 1),
-                                  (int)y - (iGrowRadius + 1),
-                                  (iGrowRadius + 1) * 2,
-                                  (iGrowRadius + 1) * 2);
-                }
-              else
-                {
-                  circlefill (_env->db, (int)x, (int)y, expSize - 2, makecol (255, 0, 0));
-                  circle(_env->db, (int)x, (int)y, expSize - 1, makecol(255, 150, 0));
-                  circle(_env->db, (int)x, (int)y, expSize, makecol(255, 150, 0));
-                  setUpdateArea ((int)x - (expSize + 1), (int)y - (expSize + 1), (expSize + 1) * 2, (expSize + 1) * 2);
-                }
-              requireUpdate ();
-            }   // end of napalm
-
-          // try drawing a funky bomblet
-          else if ( ( type == FUNKY_BOMBLET) || (type == FUNKY_DEATHLET) )
-          {
-              circlefill(_env->db, (int) x, (int) y, 4, funky_colour );
-              circle(_env->db, (int) x, (int) y, 5, makecol(0, 0, 0) );
-              setUpdateArea( (int) x - 10, (int) y - 10, 20, 20);
-              requireUpdate();
-          } 
-          // draw anything else, besides napalm
-          else
-            {
-              VIRTUAL_OBJECT::draw (dest);
-            }
-        }
-    }
-}
 
+/// === Private methods ===
+///=========================
 
-void MISSILE::setEnvironment(ENVIRONMENT *env)
-    {
-      if (!_env || (_env != env))
-        {
-          _env = env;
-          _index = _env->addObject (this);
-        }
-    }
 
+/// @brief little helper struct to fire SDI lasers in a fairer way
+struct sSDI
+{
+	int32_t am    = 0;
+	double  dist  = 0.;   // Distance used for sorting
+	double  lvl   = 0.;   // AI level, human players are counted as deadly.
+	sSDI*   next  = nullptr;
+	double  range = 100.; // The more SDI, the further the shot
+	TANK*   tank  = nullptr;
+	double  x     = 0.;
+	double  y     = 0.;
+
+};
 
 
-int MISSILE::isSubClass (int classNum)
+// Check to see if any tanks have SDI defense. If they
+// do, then see if this missile should be shot down.
+// Returns the shooting tank if a shot is to be fired
+// or NULL if no tank will shoot.
+/* Update:
+ * -------
+ * Check_SDI() can do it all on its own and is now called from
+ * triggerTest() if (and *only* if) the missile isn't going off
+ * anyway.
+ * This allows SDI to be spared on missiles that are exploding anyway.
+ * Further it makes the game loop much simpler.
+ * - Sven
+ *
+ * Update:
+ * -------
+ * If an SDI is really considered to fire, a MIND SHOT is now used to
+ * determine where the missile will explode, or whether it'll miss.
+ * Tests showed, that it will result in a lower average number of
+ * iterations than the old per-pixel check. And as a nice side effect,
+ * the result is many times more accurate. ;-)
+ * - Sven
+*/
+void MISSILE::Check_SDI()
 {
-  if (classNum == MISSILE_CLASS)
-    return (TRUE);
-  else
-    return (FALSE);
-  //return (PHYSICAL_OBJECT::isSubClass (classNum));
+	static sSDI sdi[MAXPLAYERS];
+
+	// The Predictor don't checks itself:
+	if (SDI_PREDICTOR == ai_level)
+		return;
+
+	// can't shoot jelly, dirt balls, digging projectiles
+	// and anything with submunition.
+	if ( (PT_DIGGING == physType)
+	  || (weapType == NAPALM_JELLY)
+	  || ( (weapType >= DIRT_BALL) && (weapType <= SMALL_DIRT_SPREAD) )
+	  || (weap->submunition > 0) )
+		return;
+
+	// Funky Floats are "invisible" with a chance of 50%
+	if ( (PT_FUNKY_FLOAT == physType)
+	  && (rand() % 2) )
+		return;
+
+	// Reset SDI list:
+	for (int32_t i = 0; i < MAXPLAYERS; ++i)
+		sdi[i].next = nullptr;
+
+	// Create SDI list
+	TANK*   lt       = nullptr;
+	bool    shotDown = false;
+	sSDI*   pSDI     = nullptr;
+	int32_t idx      = 0;
+
+	global.getHeadOfClass(CLASS_TANK, &lt);
+	while (lt) {
+		/* A tank is not considered for SDI shots if:
+		 * 1 The tank is destroyed (obviously)
+		 * 2 The tank is this missiles owners tank
+		 * 3 The tank is flying
+		 * 4 The tank already has an SDI beam firing
+		 * 5 The SDI has no shots left for this sequence
+		 * 6 The SDI beam would shoot downwards.
+		 */
+		if ( !lt->destroy                                      // 1
+		  && (lt->player != player)                            // 2
+		  && !lt->isFlying()                                   // 3
+		  && !lt->player->sdi_has_fired.load(ATOMIC_READ)      // 4
+		  && (lt->player->ni[ITEM_SDI] > lt->player->sdiShots) // 5
+		  && ( (lt->y - 10.) >= y) ) {                         // 6
+			double startX = lt->x;
+			double startY = lt->y - 10.;
+		  	sdi[idx].am    = lt->player->ni[ITEM_SDI] - lt->player->sdiShots;
+			sdi[idx].lvl   = static_cast<double>(
+			                            ( ( lt->player->type == HUMAN_PLAYER )
+			                           || ( lt->player->type  > DEADLY_PLAYER) )
+			                            ? DEADLY_PLAYER : lt->player->type);
+			sdi[idx].tank  = lt;
+			sdi[idx].range = static_cast<double>(SDI_DISTANCE)
+			               + ( static_cast<double>(sdi[idx].am - 1)
+			                 * 2.5);
+			sdi[idx].dist  = FABSDISTANCE2(x, y, startX, startY);
+			sdi[idx].x     = startX;
+			sdi[idx].y     = startY;
+
+			/* Add the SDI to the list if:
+			 * 1: The missile is within maximum range
+			 * 2: but further away than the minimum distance and
+			 * 3: no dirt is between the gun top and the missile.
+			 */
+			if ( (sdi[idx].dist <= sdi[idx].range)                    // 1
+			  && (sdi[idx].dist >  lt->player->ni[ITEM_SDI])          // 2
+			  && !checkPixelsBetweenTwoPoints(&startX, &startY, x, y) /* 3 */ ) {
+				// This can be added!
+				if (pSDI) {
+					// Must be sorted in
+					sSDI* curr = pSDI;
+					while (curr->next && (curr->next->dist < sdi[idx].dist) )
+						curr = curr->next;
+					// Now curr->next is either nullptr or is farther away.
+					sdi[idx].next = curr->next;
+					curr->next    = &sdi[idx];
+				} else
+					// It is the new head
+					pSDI = &sdi[idx];
+				++idx;
+			} // end of in range
+		} // End of having SDI
+		lt->getNext(&lt);
+	} // End of looping tanks
+
+	// Move through the sorted list of SDI stations and see whether anybody
+	// can shoot this one down.
+	while (!shotDown && pSDI) {
+		// 20% base chance with +1% per SDI over one.
+		if ( (rand() % 100) < (19 + pSDI->am) ) {
+			// Try to predict the coordinates where the missile will go down:
+			MISSILE mind_shot(player, x, y, xv, yv, weapType, MT_MIND_SHOT,
+			                  SDI_PREDICTOR);
+
+			// Adapt missile drag if the player has dimpled/slick projectiles
+			if (player->ni[ITEM_DIMPLEP])
+				mind_shot.drag *= item[ITEM_DIMPLEP].vals[0];
+			else if (player->ni[ITEM_SLICKP])
+				mind_shot.drag *= item[ITEM_SLICKP].vals[0];
+
+			// Keep flying/rolling/digging/whatever until the missile hits something
+			// or the tank is out of explosion range
+			double   x_dist    = pSDI->x - mind_shot.x;
+			double   y_dist    = pSDI->y - mind_shot.y;
+			double   x_vel     = 0.;
+			double   y_vel     = 0.;
+			uint32_t max_range = pSDI->lvl
+			                   * std::max(ROUND(pSDI->range), weap->radius);
+			mind_shot.getVelocity(x_vel, y_vel);
+
+			// Apply physics until the missile is either destroyed, or
+			// it is moving away from the tank and the tank is outside
+			// the blast radius.
+			while (!mind_shot.destroy
+			    && ( (SIGN(x_dist) == SIGN(x_vel))
+			      || (SIGN(y_dist) == SIGN(y_vel))
+			      || (  ABSDISTANCE2(pSDI->x, pSDI->y, mind_shot.x, mind_shot.y)
+			          < max_range) ) ) {
+				mind_shot.applyPhysics();
+				x_dist = pSDI->x - mind_shot.x;
+				y_dist = pSDI->y - mind_shot.y;
+				mind_shot.getVelocity(x_vel, y_vel);
+			}
+
+			// If the missile is destroyed, check whether the explosion would
+			// a) hit this tank and be) be nearer than the missile is now.
+			// If so, shoot it down!
+			bool will_hit = false;
+			if (mind_shot.destroy) {
+				int32_t x_rad = DRILLER == weapType
+				              ? weap->radius / 20
+				              : weap->radius;
+				int32_t y_rad = ( (SHAPED_CHARGE <= weapType)
+				               && (CUTTER        >= weapType) )
+				              ? weap->radius / 20
+				              : weap->radius;
+
+				if ( (std::abs(x_dist) <= x_rad) // tank in x range
+				  && (std::abs(y_dist) <= y_rad) // tank in y range
+				  && ( ( std::abs(x - pSDI->x) > x_rad) // misses x radius now
+				    || ( std::abs(y - pSDI->y) > y_rad) // misses y radius now
+						// Is now farther away than when it goes off:
+				    || (   ABSDISTANCE2(x, y, pSDI->tank->x, pSDI->tank->y)
+				        >= ABSDISTANCE2(x, y, mind_shot.x, mind_shot.y) ) ) )
+					will_hit = true;
+			}
+
+			if (will_hit) {
+				shotDown = true;
+
+				// The actual shooting is only done if this is no mind shot
+				if (MT_MIND_SHOT != missileType) {
+					lt = pSDI->tank;
+
+					// The player has a 1% chance per SDI (with 50% max)
+					// that one of the lasers burns out.
+					// The chance can become this high to prevent players with
+					// few missiles to shoot down to buy hundreds of SDI units.
+					int32_t chance = pSDI->am > 50 ? 50 : pSDI->am;
+					bool    burnt  = (rand() % 100) < chance ? true : false;
+
+					try {
+						new BEAM(lt->player, pSDI->x, pSDI->y, x, y,
+									pSDI->am > 50 ? LRG_LAZER :
+									pSDI->am > 25 ? MED_LAZER :
+										SML_LAZER, burnt);
+
+						trigger();
+
+						if ( burnt )
+							pSDI->tank->player->ni[ITEM_SDI]--;
+
+						pSDI->tank->player->sdi_has_fired.store(true, ATOMIC_WRITE);
+
+					} catch (...) {
+						// Just not shot down. ;)
+						shotDown = false;
+					}
+				} else
+					// But the bot needs to know that its shot ends here
+					destroy = true;
+			} // end of not missing
+		}
+		pSDI = pSDI->next;
+	} // End of going through SDI list
 }
 
 
 // This function returns the distance above ground of
 // the missile.
-int MISSILE::Height_Above_Ground()
+int32_t MISSILE::Height_Above_Ground()
 {
-  int height = (int)y + 1;
+	int32_t rx = ROUND(x);
+
+	if ( (rx < 1) || (rx >= env.screenWidth) )
+		return -1;
+
+	double  sx = xv / yv;
+	double  px = x + sx;
+	double  py = y + 1.;
+	int32_t height = 1;
+
+	while ( (py < env.screenHeight)
+		 && (px > .9)
+		 && (px < (env.screenWidth - .9) )
+		 && ( (py < BOXED_TOP )
+		   || (PINK == getpixel(global.terrain, px, py) ) ) ) {
+		px += sx;
+		py += 1.;
+		++height;
+
+		// If this is a wrapping wall, px must be wrapped of course
+		if (WALL_WRAP == env.current_wallType) {
+			if (px < 1.)
+				px = env.screenWidth - 1. - (1. - std::abs(px));
+			if (px > (env.screenWidth - 1.) )
+				px = 1 + (env.screenWidth - 1. - px);
+		}
+	}
+
+	return height;
+}
 
-  if (y < 0)
-    return height;
 
-  height = _env->surface[(int) x] - y;
-  return height;
+// Modify xv/yv according to repulse shields in the vicinity of the missile
+void MISSILE::Repulse_Missile()
+{
+	TANK*  lt     = nullptr;
+	double xaccel = 0;
+	double yaccel = 0;
+
+	global.getHeadOfClass(CLASS_TANK, &lt);
+
+	while (lt) {
+		if ( !lt->destroy && (lt->player != player) ) {
+
+			if (lt->repulse (x + xv, y + yv, &xaccel, &yaccel, physType)) {
+				xv += xaccel;
+				yv += yaccel;
+			}
+		}
+		lt->getNext(&lt);
+	}
 }
 
+void MISSILE::trigger ()
+{
+	// Create explosion
+	try {
+		new EXPLOSION (player, x, y, xv, yv, weapType, isWeaponFire);
+	} catch (...) {
+		perror ( "missile.cpp: Failed allocating memory for explosion in MISSILE::trigger");
+	}
+
+	// If the explosion is near a wrapping wall, a second "fake"
+	// explosion must be generated to display the wrapping effect
+	if ( (WALL_WRAP == env.current_wallType) && (weapType < WEAPONS) ) {
+		int32_t left   = 0;
+		int32_t top    = 0;
+		int32_t x_rad  = weapon[weapType].radius;
+		int32_t y_rad  = weapon[weapType].radius;
+
+		// Driller is smaller
+		if (DRILLER == weapType)
+			x_rad /= 20;
+
+		// shaped charges are flatter
+		if ( (SHAPED_CHARGE <= weapType) && (CUTTER >= weapType) )
+			y_rad /= 20;
+
+		// Set wrapped x position
+		if (x < x_rad)
+			left = env.screenWidth + x;
+		else if (x > (env.screenWidth - x_rad))
+			left = x - env.screenWidth;
+
+		// (possibly) set wrapped y position
+		if (env.isBoxed && env.do_box_wrap) {
+			if (y < (y_rad + MENUHEIGHT) )
+				top = env.screenHeight + y;
+			else if (y > (env.screenHeight - y_rad))
+				top = y - env.screenHeight + MENUHEIGHT;
+		}
+
+		// If an x/y-position is found, generate second explosion:
+		if ( left || top ) {
+			int32_t new_x = left ? left : x;
+			int32_t new_y = top  ? top  : y;
+			try {
+				new EXPLOSION (player, new_x, new_y, xv, yv, weapType, isWeaponFire);
+			} catch (...) {
+				perror ( "missile.cpp: Failed allocating memory for secondary explosion in MISSILE::trigger");
+			}
+		}
+	}
+
+	destroy = true;
+
+	if (weapType < SML_METEOR)
+		play_explosion_sound(weapType, x, 255, 1000);
+	else
+		play_natural_sound(weapType, x, 255, 1000);
+}
 
 
-// Check to see if any tanks have SDI defense. If they
-// do, then see if this missile should be shot down.
-// Returns the shooting tank if a shot is to be fired
-// or NULL if no tank will shoot.
-TANK *MISSILE::Check_SDI(GLOBALDATA *global)
+void MISSILE::triggerTest ()
 {
-    TANK *my_tank;
-    int index;
-    double distance;
-    int chance;
-
-    if (type == NAPALM_JELLY)     // can't shoot jelly
-       return NULL;
-
-    for (index = 0; index < global->numPlayers; index++)
-    {
-        if ( ( global->players[index] ) && ( global->players[index]->tank) )
-        {
-            my_tank = global->players[index]->tank;
-            if ( (my_tank->player->ni[ITEM_SDI]) && (my_tank->player != this->player) )
-            {
-                 distance = pow(x - my_tank->x, 2);
-                 distance += pow(y - my_tank->y, 2);
-                 distance = sqrt(distance);
-                 // only fire if within range and above the tank
-                 if ( ( distance < SDI_DISTANCE ) && (my_tank->y > y) )
-                 {
-                      chance = rand() % 5;
-                      if (! chance)
-                      {
-                         return my_tank;
-                      }
-                 }        // within range
-            }      // we have SDI
-        }       // end of we have a valid player/tank
-       
-    }           // finished going through all players
-    
-    return NULL;
+	bool    quell       = noimpact ? true : false;
+
+	// No tests are needed, if a too high velocity has
+	// just lacerated the projectile.
+	if ( lacerated ) {
+		if ( (MT_MIND_SHOT == missileType) || quell)
+			// Only end of performance is needed to know.
+			destroy = true;
+		else
+			trigger();
+		return;
+	}
+
+	TANK*   lt          = nullptr;
+	double  old_delta_x = xv;
+
+	// Has it hit a tank?
+	global.getHeadOfClass(CLASS_TANK, &lt);
+	while (lt) {
+		if ( !lt->destroy
+		  && lt->isInBox(x, y, x, y) ) {
+			hitSomething = true;
+			if (MT_MIND_SHOT != missileType)
+				lt->requireUpdate ();
+
+			// Be sure the detonation does not take place
+			// on the gun top.
+			if (y < lt->y)
+				y = lt->y; // I think we can live with this 'shift'.
+		}
+		lt->getNext(&lt);
+	}
+
+	// Unless quelled, check what is to be done
+	bool do_check = (!quell && (y > MENUHEIGHT));
+
+	// Only really check if something is hit, the y coordinate is through the
+	// floor or a non-PINK pixel is in the way:
+	if (do_check && !hitSomething && (y < env.screenHeight)) {
+		// neither hit nor floor crunch, check the pixel:
+		int32_t round_x = ROUND(x);
+		int32_t round_y = ROUND(y);
+		if ( (round_x >= 0) && (round_x < env.screenWidth) ) {
+			do_check = (PINK != getpixel(global.terrain, round_x, round_y));
+		}
+	} // End of pixel check
+
+	// Only continue if all checks passed:
+	if (do_check) {
+
+		// A roller changes from flying to rolling
+		if ( (weapType >= SML_ROLLER)
+		  && (weapType <= DTH_ROLLER)
+		  && (PT_NORMAL == physType) ) {
+
+			if (age > 1) {
+				quell    = true; // No detonation, just switch to rolling
+				physType = PT_ROLLING;
+				age      = 0;
+
+				// Set rolling start position and initial movement
+				y -= 5;
+				xv = 0;
+				yv = 0;
+
+				// Possibly fix x
+				if (x <= 1) {
+					x  = 1;
+					xv = 1;
+				} else if (x >= (env.screenWidth - 2)) {
+					x  =  env.screenWidth - 2;
+					xv = -1;
+				}
+
+				// Possibly fix y
+				int32_t round_x = ROUND(x);
+				int32_t surf_y  = global.surface[round_x].load(ATOMIC_READ);
+				if ( (y >=  surf_y)         // y is surface or below
+				  && (y <= (surf_y + 2) ) ) // but not buried more than 2 px
+					y = surf_y - 1;
+
+				// Set movement if not done already:
+				if ( (xv > -0.9) && (xv < 0.9) ) {
+					bool can_go_left  = (round_x > 3);
+					bool can_go_right = (round_x < (env.screenWidth - 4));
+
+					if (can_go_left || can_go_right) {
+						if (can_go_left)
+							can_go_left  = (PINK == getpixel(global.terrain,
+							                                     round_x - 1, y));
+						if (can_go_right)
+							can_go_right = (PINK == getpixel(global.terrain,
+							                                     round_x + 1, y));
+					} // End of checking direction pixels
+
+					if (can_go_left && can_go_right)
+						// Prefer old movement direction
+						xv = SIGN(old_delta_x);
+					else if (can_go_left)
+						xv = -1;
+					else if (can_go_right)
+						xv = 1;
+					else
+						// nothing worked, both paths are blocked.
+						xv = rand() % 2 ? -1 : 1;
+				}
+
+				// If the roller is hammered into a wall, detonate it
+				if ( ( (WALL_STEEL == env.current_wallType)
+				    && ( (x <= 2) || (x >= (env.screenWidth - 3) ) ) )
+				  || (env.isBoxed
+				    && (y <= MENUHEIGHT)
+				    && ( ( (WALL_WRAP  == env.current_wallType)
+				        && ( !env.do_box_wrap
+				          || (  global.surface[ROUND(x)].load(ATOMIC_READ)
+				              < env.screenHeight) ) )
+				      || (WALL_STEEL == env.current_wallType) ) ) ) {
+					quell        = false;
+					hitSomething = true;
+				}
+			} // End of switching roller physics
+		} // End of roller handling
+
+		// A burrower or penetrator changes from flying to digging
+		else if ( (weapType == BURROWER) || (weapType == PENETRATOR) ) {
+			if (PT_DIGGING == physType)
+				// If it hit, it must not be quelled.
+				quell = !hitSomething;
+			else if (PT_NORMAL == physType) {
+				// This is the switch
+				physType = PT_DIGGING;
+				quell    = true;
+				xv      *= 0.1;
+				yv      *= 0.1;
+				age      = 0;
+			}
+		} // End of burrower handling
+
+		// If a weapon has submunition, it goes off now
+		else if (weap->submunition >= 0) {
+			quell = true; // This one is done
+
+			if ( (weap->numSubmunitions > 0) && (MT_MIND_SHOT != missileType) ) {
+				WEAPON*   submunition     = &weapon[weap->submunition];
+				double    divergenceStep  = static_cast<double>(weap->divergence)
+				                          / static_cast<double>(weap->numSubmunitions - 1);
+				int32_t   startPoint      = divergenceStep < 0. ? 0 : 180;
+				int32_t   randStart       = rand () % 1000000;
+				ePhysType submunitionPhys = PT_NORMAL;
+				double    inheritedXV     = weap->impartVelocity * xv;
+				double    inheritedYV     = weap->impartVelocity * yv;
+				int32_t   startY          = y - 20;
+				bool      ceiling_crash   = false;
+
+				// See whether the weapon was fired into a ceiling:
+				// This applies for both steel ceilings and wrap ceilings,
+				// but the latter only if no ceiling wrap is activated or
+				// if the next pixel at the bottom is dirt.
+				if (env.isBoxed && (startY <= MENUHEIGHT) // Base condition
+				  && ( ( (WALL_WRAP  == env.current_wallType)
+					  && (!env.do_box_wrap // <- No wrap makes it steel
+						// \/ dirt makes the ceiling unwrapable
+				        || (  global.surface[ROUND(x)].load(ATOMIC_READ)
+				            < env.screenHeight) ) )
+				    || (  WALL_STEEL == env.current_wallType) ) ) { // This always blasts
+					ceiling_crash = true;
+					// If the weapon is fired into a ceiling, adapt starting y
+					startY = MENUHEIGHT + 20;
+				}
+
+				// if napalm is going off, play its burn out sound
+				if ( (weapType >= SML_NAPALM) && (weapType <= LRG_NAPALM))
+					play_explosion_sound(weapType, x, 128 + (weap->radius / 2), 1000);
+
+				// Change physics of the submunitions for the funky bombs
+				if ( (weapType == FUNKY_BOMB) || (weapType == FUNKY_DEATH) )
+					submunitionPhys = PT_FUNKY_FLOAT;
+
+				// If this is a steel wall hit, the start point angle needs
+				// to be adapted.
+				if ( (WALL_STEEL == env.current_wallType) && !ceiling_crash) {
+					if ( (CLUSTER <= weapType) && (SUP_CLUSTER >= weapType) ) {
+						if ( x < 2 )
+							startPoint -= weap->divergence + 1 + (rand() % 10);
+						else if ( x > (env.screenWidth - 3) )
+							startPoint += weap->divergence + 1 + (rand() % 10);
+					} else if ( (SML_NAPALM <= weapType)
+					         && (LRG_NAPALM >= weapType) ) {
+						if ( x < 2 )
+							startPoint -= 10 + rand() % 21;
+						else if ( x > (env.screenWidth - 3) )
+							startPoint += 10 + rand() % 21;
+					}
+				}
+
+				// If this is a ceiling crash, the start point angle needs
+				// to be erased.
+				if (ceiling_crash) {
+					startPoint = 0;
+					if ( (SMALL_MIRV == weapType)
+					  || (CLUSTER_MIRV == weapType) )
+						inheritedYV = std::abs(inheritedYV);
+				}
+
+				// The spread can be created!
+				for (int32_t sc = 0; sc < weap->numSubmunitions; ++sc) {
+					MISSILE *newmis       = nullptr;
+					double   launchSpeed  = weap->launchSpeed;
+					int32_t  newMissCount = submunition->countdown;
+					int32_t  newMissAngle = ROUND(
+								  (divergenceStep * static_cast<double>(sc))
+								+  static_cast<double>(startPoint)
+								- (static_cast<double>(weap->divergence) / 2.) );
+
+					// Manipulate angle if applicable
+					if (weap->spreadVariation > 0.)
+						newMissAngle += ROUND(
+									  static_cast<double>(weap->divergence)
+									* weap->spreadVariation
+									* Noise(randStart + 1054 + sc) );
+
+					// Be sure the angle is valid
+					while (newMissAngle < 0)
+						newMissAngle += 360;
+					newMissAngle %= 360;
+
+					// Manipulate number of submunition projectiles if applicable
+					if (submunition->countVariation > 0) {
+						newMissCount += ROUND(
+									  static_cast<double>(submunition->countdown)
+									* submunition->countVariation
+									* Noise(randStart + 78689 + sc) );
+						// This might go wrong, so be sure it doesn't
+						if (newMissCount <= 0)
+							newMissCount = 0;
+					}
+
+					// Manipulate launching speed if applicable
+					if (weap->speedVariation > 0)
+						launchSpeed += ROUND(
+									  weap->speedVariation
+									* weap->launchSpeed
+									* Noise(randStart + 124786 + sc) );
+
+					// Launch new submunition missile
+					try {
+						newmis = new MISSILE (player, x, startY,
+									env.slope[newMissAngle][0]
+									* launchSpeed
+									* env.FPS_mod
+									+ inheritedXV,
+									env.slope[newMissAngle][1]
+									* launchSpeed
+									* env.FPS_mod
+									+ inheritedYV, weap->submunition,
+									missileType, ai_level);
+						newmis->physType  = submunitionPhys;
+						newmis->countdown = newMissCount;
+						newmis->setUpdateArea(newmis->x - 20, newmis->y - 20, 40, 40);
+					} catch (...) {
+						perror ( "missile.cpp: Failed to allocate memory for"
+								 "newmis in MISSILE::triggerTest (CLUSTER)");
+					}
+				} // End of looping submunitions
+			} // End of having submunition count
+
+			destroy = true;
+		} // End of having a submunition type defined
+
+	}
+
+	// If a countdown was set and this is old enough, this missile is no
+	// longer quelled, regardless of what this means for this weapon type
+	if ( (countdown >= 0) && (age >= countdown) )
+		quell = false;
+
+	// Riot charges always go of in an instant.
+	if ( (weapType >= RIOT_CHARGE) && (weapType <= RIOT_BLAST) ) {
+		quell   = false;
+		destroy = true;
+	}
+
+
+	// Eventually trigger missiles that hit something or are lacerated
+	if (hitSomething && !quell) {
+		if (MT_MIND_SHOT == missileType)
+			destroy = true;
+		else
+			trigger();
+	} else if ( (yv > 0.)
+	         && ( (weapType < RIOT_BOMB) || (weapType > SMALL_DIRT_SPREAD) ) )
+		// Otherwise it is time to check whether any tank SDI shoots it down
+		Check_SDI();
 }
 
 
+/// @brief special method to update private members iof sub munition missiles.
+/// This method is only interesting for AICore tracing clusters.
+void MISSILE::update_submun(ePhysType p_type, int32_t cnt_down)
+{
+	physType  = p_type;
+	countdown = cnt_down;
+}
+
diff --git a/src/missile.h b/src/missile.h
index db78512..0c5630d 100644
--- a/src/missile.h
+++ b/src/missile.h
@@ -24,51 +24,85 @@
 #include "main.h"
 #include "physobj.h"
 
-#define MAX_MISSLE_AGE 20
-#define MAX_METEOR_AGE 5
+// The ages are *seconds* and transformed to frames in the ctor.
+#define MAX_JELLY_AGE    1
+#define MAX_MISSILE_AGE 15
+#define MAX_METEOR_AGE   5
 
-#define TIGGER_HEIGHT 300
+#define SDI_DISTANCE   100
+#define TRIGGER_HEIGHT 300
+
+
+/** @enum eMissileType
+  * @brief Determines what kind of weapon is shot
+**/
+enum eMissileType
+{
+	MT_WEAPON = 0, //!< Normal weapon, nothing special
+	MT_ITEM,       //!< Not a weapon but an item
+	MT_NATURAL,    //!< Fired by natural disaster, like meteors and dirt balls.
+	MT_MIND_SHOT   //!< AI thinking.
+};
 
-#define SDI_DISTANCE 100
 
 class MISSILE: public PHYSICAL_OBJECT
-  {
-  private:
-    // New values for growing napalm jelly:
-    int iGrowRadius;
-    int bIsGrowing;
-
-  public:
-    int	countdown;
-    int	expSize;
-    int	etime;
-    int	damage;
-    int	eframes;
-    int	picpoint;
-    int	type;
-    int	sound;
-    int funky_colour;
-    WEAPON	*weap;
-
-    virtual ~MISSILE ();
-    MISSILE (GLOBALDATA *global, ENVIRONMENT *env, double xpos, double ypos, double xvel, double yvel, int weaponType);
-    void	draw (BITMAP *dest);
-    int	triggerTest ();
-    void	trigger ();
-    // void	explode ();
-    int	applyPhysics ();
-    void	initialise ();
-    int	isSubClass (int classNum);
-    void setEnvironment(ENVIRONMENT *env);
-
-    inline virtual int	getClass ()
-    {
-      return (MISSILE_CLASS);
-    }
-
-    int     Height_Above_Ground ();
-    TANK *Check_SDI(GLOBALDATA *global);       // see if missile should be shot down
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit MISSILE (PLAYER* player_, double xpos, double ypos,
+	                  double xvel, double yvel,
+	                  int32_t weapon_type, eMissileType missile_type,
+	                  int32_t ai_level_);
+	~MISSILE ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void    applyPhysics();
+	int32_t bounced     () const;
+	int32_t direction   () const;
+	void    draw        ();
+
+	eClasses getClass() { return CLASS_MISSILE; }
+
+	void    update_submun(ePhysType p_type, int32_t cnt_down);
+
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void    Check_SDI(); // see if missile should be shot down
+	int32_t Height_Above_Ground();
+	void    Repulse_Missile();
+	void    trigger     ();
+	void    triggerTest ();
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	int32_t       ai_level     =  0; // Level of the AI shooting a mind shot
+	int32_t       countdown    = -1;
+	int32_t       funky_colour = BLACK;
+	int32_t       growRadius   =  0;
+	bool          isGrowing    = false;
+	eMissileType  missileType  = MT_WEAPON;
+	WEAPON*       weap         = nullptr;
+};
 
 #endif
 
diff --git a/src/network.cpp b/src/network.cpp
index e3bff5a..a8cdb48 100644
--- a/src/network.cpp
+++ b/src/network.cpp
@@ -1,3 +1,7 @@
+#include "network.h"
+#include "player.h"
+#include "globaldata.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -6,16 +10,15 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/select.h>
+#if defined(ATANKS_IS_BSD)
+#  include <arpa/inet.h>
+#  include <netinet/in.h>
+#endif // BSD
 #include <netdb.h>
 #include <errno.h>
 #include <unistd.h>
-#include <pthread.h>
 #endif
 
-#include "network.h"
-#include "player.h"
-#include "globaldata.h"
-
 
 // init the object
 MESSAGE_QUEUE::MESSAGE_QUEUE()
@@ -45,7 +48,7 @@ bool MESSAGE_QUEUE::Add(char *some_text, int to)
     new_message = (MESSAGE *) calloc(1, sizeof(MESSAGE));
     if (! new_message)
         return false;
-    new_message->text = (char *) calloc( strlen(some_text), sizeof(char) );
+    new_message->text = (char *) calloc( strlen(some_text) + 1, sizeof(char) );
     if (! new_message->text)
     {
        free(new_message);
@@ -53,7 +56,7 @@ bool MESSAGE_QUEUE::Add(char *some_text, int to)
     }
 
     // fill the message structure
-    strcpy(new_message->text, some_text);
+	strncpy(new_message->text, some_text, strlen(some_text));
     new_message->to = to;
     // next is already cleared by calloc
 
@@ -103,7 +106,7 @@ MESSAGE *MESSAGE_QUEUE::Peek()
    my_message = (MESSAGE *) calloc( 1, sizeof(MESSAGE) );
    if (! my_message)
       return NULL;
-   my_message->text = (char *) calloc( strlen(first_message->text), sizeof(char) );
+   my_message->text = (char *) calloc( strlen(first_message->text) + 1, sizeof(char) );
    if (! my_message->text)
    {
       free(my_message);
@@ -111,9 +114,9 @@ MESSAGE *MESSAGE_QUEUE::Peek()
    }
 
    // we have an empty message. Now fill it
-   strcpy(my_message->text, first_message->text);
+   strncpy(my_message->text, first_message->text, strlen(first_message->text));
    my_message->to = first_message->to;
-   
+
    return my_message;
 }
 
@@ -149,7 +152,7 @@ MESSAGE *MESSAGE_QUEUE::Read_To(int my_to)
     if (! my_message)
         return NULL;
 
-    my_message->text = (char *) calloc( strlen(current->text), sizeof(char));
+    my_message->text = (char *) calloc( strlen(current->text) + 1, sizeof(char));
     if (! my_message->text)
     {
         free(my_message);
@@ -157,7 +160,7 @@ MESSAGE *MESSAGE_QUEUE::Read_To(int my_to)
     }
 
     my_message->to = current->to;
-    strcpy(my_message->text, current->text);
+	strncpy(my_message->text, current->text, strlen(current->text));
     if (previous)
        previous->next = current->next;
     else
@@ -234,16 +237,16 @@ int Setup_Server_Socket(int port)
 {
    int listensocket;
    struct sockaddr_in myaddr;
-   
+
    listensocket = socket(AF_INET, SOCK_STREAM, 0);
    myaddr.sin_port = htons(port);
    myaddr.sin_addr.s_addr = INADDR_ANY;
-   if (bind(listensocket, (struct sockaddr *) &myaddr, sizeof(myaddr)) < 0) 
+   if (bind(listensocket, (struct sockaddr *) &myaddr, sizeof(myaddr)) < 0)
    {
        printf("Bind failed: %s\n", strerror(errno));
        return -1;
    }
-   if (listen(listensocket, 5)) 
+   if (listen(listensocket, 5))
    {
        printf("Listen failed: %s\n", strerror(errno));
        return -1;
@@ -431,21 +434,23 @@ int Check_For_Errors(int socket_number)
 // This function will probably be called as a separate thread at the
 // start of the game. It will set up a listening port, accept
 // incoming connections and manage them. That is, they will be
-// passed on to AI players. 
+// passed on to AI players.
 void *Send_And_Receive(void *all_the_data)
 {
    SEND_RECEIVE_TYPE *send_receive_data = (SEND_RECEIVE_TYPE *) all_the_data;
    int server_socket, new_socket;
    int status, counter;
-   GLOBALDATA *global = (GLOBALDATA *) send_receive_data->global;
+
    bool found;
+	char buffer[7] = { 0 };
+	int32_t towrite, written;
 
    // set up listening socket
    server_socket = Setup_Server_Socket(send_receive_data->listening_port);
    if (server_socket == -1)
    {
       printf("Error creating listening socket.\n");
-      pthread_exit(NULL);
+      return nullptr;
    }
 
    while (! send_receive_data->shut_down)
@@ -459,16 +464,16 @@ void *Send_And_Receive(void *all_the_data)
          // give connection to AI player
          found = false;
          counter = 0;
-         while ( (! found) && (counter < global->numPlayers) )
+         while ( (! found) && (counter < env.numGamePlayers) )
          {
-              if ( ( global->players[counter]->type >= USELESS_PLAYER) &&
-                   ( global->players[counter]->type <= DEADLY_PLAYER) )
+              if ( ( env.players[counter]->type >= USELESS_PLAYER) &&
+                   ( env.players[counter]->type <= DEADLY_PLAYER) )
               {
                   found = true;
-                  global->players[counter]->server_socket = new_socket;
-                  global->players[counter]->previous_type = global->players[counter]->type;
-                  global->players[counter]->type = NETWORK_CLIENT;
-                  printf("Assigned connection to %s\n", global->players[counter]->getName() );
+                  env.players[counter]->server_socket = new_socket;
+                  env.players[counter]->previous_type = env.players[counter]->type;
+                  env.players[counter]->type = NETWORK_CLIENT;
+                  printf("Assigned connection to %s\n", env.players[counter]->getName() );
               }
               else
                  counter++;
@@ -477,30 +482,30 @@ void *Send_And_Receive(void *all_the_data)
          if (! found)
          {
             printf("Unable to assign new connection to player.\n");
-            write(new_socket, "NOROOM", strlen("NOROOM") );
+			SAFE_WRITE(new_socket, "%s", "NOROOM")
             close(new_socket);
          }
       }
-     
+
       // consider resting for a moment?
-      usleep(10000);
+      LINUX_SLEEP
    }
 
    // clean up everything
    printf("Cleaning up networking\n");
    Clean_Up_Server_Socket(server_socket);
    counter = 0;
-   while (counter < global->numPlayers)
+   while (counter < env.numGamePlayers)
    {
-      if (global->players[counter]->type == NETWORK_CLIENT)
+      if (env.players[counter]->type == NETWORK_CLIENT)
       {
-          write(global->players[counter]->server_socket, "CLOSE", strlen("CLOSE") );
-          close(global->players[counter]->server_socket);
+		SAFE_WRITE(env.players[counter]->server_socket, "%s", "CLOSE")
+          close(env.players[counter]->server_socket);
       }
       counter++;
    }
 
-   pthread_exit(NULL);
+   return nullptr;
 }
 
 
diff --git a/src/network.h b/src/network.h
index 3fbdb38..7bc5c78 100644
--- a/src/network.h
+++ b/src/network.h
@@ -19,14 +19,14 @@ updated to run on other operating systems.
 
 #define MAX_MESSAGE_LENGTH 256
 
-typedef struct message_struct
+struct MESSAGE
 {
   char *text;
   int to;   // which client does the message go to? May not be used as most will go to everyone
   void *next;
-} MESSAGE;
+};
+
 
- 
 
 class MESSAGE_QUEUE
 {
@@ -52,23 +52,36 @@ public:
 
    // erase all messages in the queue
    void Erase_All();
-   
+
 };
 
 
 
-typedef struct send_receive_struct
+struct SEND_RECEIVE_TYPE
 {
     int listening_port;
     int shut_down;
-    void *global;
-} SEND_RECEIVE_TYPE;
+};
 
 
+#define MAX_CLIENTS 10
+#define DEFAULT_NETWORK_PORT 25645
+
 #ifdef NETWORK
 
-#define MAX_CLIENTS 10
-#define DEFAULT_LISTEN_PORT 25645
+
+/// Wrapper for safe socket writes with return value check.
+/// A char buffer named "buffer" must be available to put the message in.
+/// The two size_t values "towrite" and "written" must be at least declared.
+/// All three variables will be overwritten.
+#define SAFE_WRITE(sock_, fmt_, ...) { \
+	sprintf(buffer, fmt_, __VA_ARGS__); \
+	towrite = strlen(buffer); \
+	written = write(sock_, buffer, towrite); \
+	if (written < towrite) \
+		fprintf(stderr, "%s:%d: Warning: Only %d/%d bytes sent to server\n", \
+				__FILE__, __LINE__, written, towrite); \
+}
 
 int Setup_Server_Socket(int port);
 
@@ -90,6 +103,8 @@ int Check_For_Errors(int socket_number);
 
 void *Send_And_Receive(void *data_we_need);
 
+#else
+	#define SAFE_WRITE(sock_, fmt_, ...) { }
 #endif
 
 
diff --git a/src/optioncontent.h b/src/optioncontent.h
new file mode 100644
index 0000000..3068130
--- /dev/null
+++ b/src/optioncontent.h
@@ -0,0 +1,1326 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONCONTENT_H_INCLUDED
+#define ATANKS_SRC_OPTIONCONTENT_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "optiontypes.h"
+#include "globaltypes.h"
+
+/** @file optioncontent.h
+  *
+  * This file defines static string arrays with the text content of the
+  * player, play and option menu and all sub menus.
+  *
+  * It is not necessary to include this file anywhere else but in menu.cpp.
+  *
+**/
+
+
+// Maximum number of entries including Title and 0x0 termination per menu
+const uint32_t maxEntriesPerMenu = 18;
+
+
+// Maximum text entries per text class including 0x0 termination
+const uint32_t maxEntriesPerClass = 11;
+
+
+/** @brief string array for the menu content
+  *
+  * The ordering, although it looks a bit overwhelming here, is quite simple.
+  * The first index is the menu class, the second is the language.
+  *
+  * With this both translation and adding new content is fairly easy. Just
+  * copy a block (after adding new enum entries at the proper places in
+  * optiontypes.h) and edit to the new content.
+  *
+  * All text arrays end with a zero 0x0 entry. It is therefore not needed to
+  * hard code any menu list sizes.
+  *
+  * As a rule of thumb, the title is the first line, every other texts are
+  * listed with two entries per line. Unless a possible third entry is the
+  * finalizing 0x0 entry, it does not need its own line.
+**/
+const char* const
+MenuTitleText[MC_MENUCLASS_COUNT][EL_LANGUAGE_COUNT][maxEntriesPerMenu] = {
+	{
+	/* -------------------- *
+	 * --- AREYOUSURE   --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Sind Sie sicher?",
+			"Ja", "Nein", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+		/* ===== Needs to be translated ===== */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+		/* ===== Needs to be translated ===== */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Are you sure?",
+			"Yes", "No", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- FINANCE      --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Money",
+			"Starting Money", "Interest Rate",
+			"Round Win Bonus", "Damage Bounty",
+			"Self-Damage Penalty", "Team-Damage Penalty", "Tank Destruction Bonus",
+			"Tank Self-Destruction Penalty", "Item Sell Multiplier",
+			"Teams Share" , "Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Dinheiro",
+			"Dinheiro inicial", "Taxa de Juros",
+			"Bônus por Vitória", "Bônus por Estrago",
+			"Penalidade por Auto-Estrago", "Team-Damage Penalty", "Bônus por Tanque Destruído",
+			"Penalidade por Auto-Destruição", "Multiplicador de Item Vendido",
+			"Parte das equipes", "Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+			"Finances",
+			"Somme de départ", "Taux d'intérêt",
+			"Gains par victoire", "Bonus dommages",
+			"Pénalité auto-dommages", "Team-Damage Penalty", "Bonus destruction tank",
+			"Pénalité autodestruction tank", "Coeff. vente item",
+			"Part d'equipes", "Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Geld",
+			"Startgeld", "Zinssatz",
+			"Rundenbonus", "Schadensbonus",
+			"Strafe für Selbstschaden", "Strafe für Teamschaden", "Zerstörungsbonus",
+			"Selbstzerstörungsstrafe", "Verkaufsmultiplikator",
+			"Mannschaftanteil", "Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Peniaze",
+			"Peniaze na začiatku",  "Úroková miera",
+			"Bonus pri skončení kola",  "Odmena za poškodenie",
+			"Pokuta za vlastné poškodenie",  "Team-Damage Penalty", "Bonus za zničenie tanku",
+			"Pokuta za vlastné zničenie tanku",  "Násobiteľ pri predaji položiek",
+			"Teamy zdieľajú peniaze", "Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Экономика",
+			"Начальные деньги", "Банковский процент",
+			"Бонус за победу", "Бонус за попадание",
+			"Штраф за попадание в себя", "Team-Damage Penalty", "Бонус за уничтожение",
+			"Штраф за самоуничтожение", "Коэфф. продажи снаряжения",
+			"Командные боеприпасы", "Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Money",
+			"Starting Money", "Interest Rate",
+			"Round Win Bonus", "Damage Bounty",
+			"Self-Damage Penalty", "Team-Damage Penalty", "Tank Destruction Bonus",
+			"Tank Self-Destruction Penalty", "Item Sell Multiplier",
+			"Teams Share" , "Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Money",
+			"Starting Money", "Interest Rate",
+			"Round Win Bonus", "Damage Bounty",
+			"Self-Damage Penalty", "Team-Damage Penalty", "Tank Destruction Bonus",
+			"Tank Self-Destruction Penalty", "Item Sell Multiplier",
+			"Teams Share", "Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- GRAPHICS     --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Graphics",
+			"Full Screen", "Dithering",
+			"Detailed Land", "Detailed Sky",
+			"Fading Text", "Shadowed Text",
+			"Swaying Text",
+			"Colour Theme", "Screen Width",
+			"Screen Height", "Mouse Pointer",
+			"Game Speed", "Custom Background",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Gráficos",
+			"Tela Cheia", "Pontilhamento",
+			"Detalhes do Terreno", "Detalhes do Céu",
+			"texto sombreado", "texto de desvanecimento",
+			"Swaying Text",
+			"tema da cor", "Largura da Tela",
+			"Altura da Tela", "Ponteiro do Rato",
+			"Velocidade do jogo", "Fundo personalizado",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+			"Graphismes",
+			"Full Screen", "Tramage",
+			"Détails du terrain", "Ciel détaillé",
+			"texte ombragé", "texte de effacement",
+			"Swaying Text",
+			"Thème de couleurs", "Largeur d'écran",
+			"Hauteur d'écran", "Curseur de souris",
+			"Vitesse du jeu", "Fond fait sur commande",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Grafik",
+			"Vollbild", "Dithering",
+			"Landdetails", "Himmeldetails",
+			"Ausblendender Text", "Schattierter Text",
+			"Schwingender Text",
+			"Farbschema", "Bildschirmbreite",
+			"Bildschirmhöhe", "Mauszeiger",
+			"Spielgeschwindigket", "Eigener Hintergrund",
+			"Zeige AI Feedback", "Dynamischer Menühintergrund",
+			"Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Grafika",
+			"Na celú obrazovku", "Rozptyl",
+			"Detaily krajiny", "Detaily oblohy",
+			"Slabnúci text", "Text s tieňom",
+			"Swaying Text",
+			"Farebná téma", "Šírka obrazovky",
+			"Výška obrazovky", "Ukazovateľ myši",
+			"Rýchlosť hry", "Vlastné pozadie",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Графика",
+			"Full Screen", "Сглаживание",
+			"Детализированный ландшафт", "Детализированное небо",
+			"Исчезающий текст", "Оттененный текст",
+			"Swaying Text",
+			"Цветовая тема", "Ширина окна игры",
+			"Высота окна игры", "Курсор в игре",
+			"Скорость игры", "Собственный фон",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Graphics",
+			"Full Screen", "Dithering",
+			"Detailed Land", "Detailed Sky",
+			"Fading Text", "Shadowed Text",
+			"Swaying Text",
+			"Colour Theme", "Screen Width",
+			"Screen Height", "Mouse Pointer",
+			"Game Speed", "Custom Background",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Graphics",
+			"Full Screen", "Dithering",
+			"Detailed Land", "Detailed Sky",
+			"Fading Text", "Shadowed Text",
+			"Swaying Text",
+			"Colour Theme", "Screen Width",
+			"Screen Height", "Mouse Pointer",
+			"Game Speed", "Custom Background",
+			"Show AI Feedback", "Dynamic Menu Background",
+			"Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- MAIN         --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Main Menu",
+			"Reset All", "Physics",
+			"Weather", "Graphics",
+			"Money", "Network",
+			"Sound", "Weapon Tech Level",
+			"Item Tech Level", "Landscape",
+			"Turn Order", "Skip AI-only play",
+			"Show FPS", "Language",
+			"Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Menu Principal",
+			"Reset All", "Física",
+			"Condições Meteorológicas", "Gráficos",
+			"Finanças", "Rede",
+			"Som", "Arma Nível Tecnológico",
+			"Artigo Nível Tecnológico", "Cenário",
+			"Ordem de Jogadas", "Continuar o Jogo Só com Robôs",
+			"Show FPS", "Língua",
+			"Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+			"Menu principal",
+			"Reset All", "Physique",
+			"Météo", "Graphismes",
+			"Finances", "Réseau",
+			"Sound", "Niveau technique armes",
+			"Niveau technique équipement", "Paysage",
+			"Ordre de passage", "Continuer le Jeu Robots seuls",
+			"Show FPS", "Langue",
+			"Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Hauptmenü",
+			"Alles zurücksetzen", "Physik",
+			"Wetter", "Grafik",
+			"Geld", "Netzwerk",
+			"Sounds", "Technologiestufe Waffen",
+			"Technologiestufe Gegenstände", "Landschaft",
+			"Reihenfolge", "Überspringe Nur-KI",
+			"FPS anzeigen", "Sprache",
+			"Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Hlavné menu",
+			"Reset All", "Fyzika",
+			"Počasie", "Grafika",
+			"Peniaze", "Sieť",
+			"Zvuk", "Tech úroveň zbraní",
+			"Tech úroveň vecí", "Krajina",
+			"Poradie","Preskočiť hru samotného PC",
+			"Show FPS", "Jazyk",
+			"Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Главное меню",
+			"Reset All", "Физика",
+			"Погода", "Графика",
+			"Экономика", "Настройки сети",
+			"Звук", "Уровень оружия",
+			"Уровень снаряжения", "Тип ландшафта",
+			"Порядок хода", "Пропускать игру компьютеров",
+			"Show FPS", "Язык (Language)",
+			"Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Main Menu",
+			"Reset All", "Physics",
+			"Weather", "Graphics",
+			"Money", "Network",
+			"Sound", "Weapon Tech Level",
+			"Item Tech Level", "Landscape",
+			"Turn Order", "Skip AI-only play",
+			"Show FPS", "Language",
+			"Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Main Menu",
+			"Reset All", "Physics",
+			"Weather", "Graphics",
+			"Money", "Network",
+			"Sound", "Weapon Tech Level",
+			"Item Tech Level", "Landscape",
+			"Turn Order", "Skip AI-only play",
+			"Show FPS", "Language",
+			"Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- NETWORK      --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Network",
+			"Check Updates",  "Networking",
+			"Listen Port", "Server Address",
+			"Server Port", "Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Network",
+			"Procurar actualizações", "Activar Rede",
+			"Número de Porta", "Server Address",
+			"Server Port", "Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+			"Network",
+			"Check Updates",  "Networking",
+			"Listen Port", "Server Address",
+			"Server Port", "Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Netzwerk",
+			"Auf Aktualisierungen prüfen", "Netzwerk",
+			"offener Port", "Serveraddresse",
+			"Server Port", "Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Sieť",
+			"Kontrola aktualizácii", "Sieťová hra",
+			"Port pre načúvanie", "Server Address",
+			"Server Port", "Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Настройки сети",
+			"Проверять обновления", "Networking",
+			"Listen Port", "Server Address",
+			"Server Port", "Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Network",
+			"Check Updates",  "Networking",
+			"Listen Port", "Server Address",
+			"Server Port", "Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Network",
+			"Check Updates",  "Networking",
+			"Listen Port", "Server Address",
+			"Server Port", "Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- PHYSICS      --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Physics",
+			"Gravity", "Viscosity",
+			"Land Slide", "Land Slide Delay",
+			"Wall Type", "Boxed Mode",
+			"Boxed Ceiling Wrapping",
+			"Violent Death", "Timed Shots",
+			"Volley Delay", "Explosion Debris",
+			"Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Física",
+			"Gravidade", "Viscosidade",
+			"Deslizamento de Terra", "Corrediça da terra atrasa",
+			"Tipo de Parede", "Modalidade encaixotada",
+			"Boxed Ceiling Wrapping",
+			"Morte violenta", "Tiro programado",
+			"Volley Delay", "Explosion Debris",
+			"Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+			"Physique",
+			"Gravité", "Viscosité",
+			"Glissements de terrain", "Délai glissements de terrain",
+			"Murs", "Enfermé dans boîte",
+			"Boxed Ceiling Wrapping",
+			"Mort violente", "Projectile synchronisé",
+			"Volley Delay", "Explosion Debris",
+			"Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Physik",
+			"Gravitation", "Reibung",
+			"Erdrutsch", "Erdrutsch Verzögerung",
+			"Wand Art", "Höhlenmodus",
+			"Höhlendeckenwarp",
+			"Gewalttätiger Tod", "Zeitlimit",
+			"Mehrfachschussverzögerung", "Explosionsschrott",
+			"Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Fyzika",
+			"Gravitácia", "Viskozita",
+			"Zosun zeme", "Zdržanie zosunu zeme",
+			"Typ steny", "Režim krabíc",
+			"Boxed Ceiling Wrapping",
+			"Krutá smrť", "Časované strely",
+			"Volley Delay", "Explosion Debris",
+			"Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Физика",
+			"Гравитация", "Сила трения",
+			"Падение земли", "Задержка падения земли",
+			"Тип стен", "Потолок",
+			"Boxed Ceiling Wrapping",
+			"Мощные взрывы танков", "Задержка выстрела",
+			"Volley Delay", "Explosion Debris",
+			"Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Physics",
+			"Gravity", "Viscosity",
+			"Land Slide", "Land Slide Delay",
+			"Wall Type", "Boxed Mode",
+			"Boxed Ceiling Wrapping",
+			"Violent Death", "Timed Shots",
+			"Volley Delay", "Explosion Debris",
+			"Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Physics",
+			"Gravity", "Viscosity",
+			"Land Slide", "Land Slide Delay",
+			"Wall Type", "Boxed Mode",
+			"Boxed Ceiling Wrapping",
+			"Violent Death", "Timed Shots",
+			"Volley Delay", "Explosion Debris",
+			"Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- PLAY         --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Select Players",
+			"Rounds", "New Game Name",
+			"or Load Game", "Load Game",
+			"Campaign", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+			"Select Players",
+			"Rounds", "New Game Name",
+			"or Load Game", "Load Game",
+			"Campaign", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+			"Select Players",
+			"Rounds", "New Game Name",
+			"or Load Game", "Load Game",
+			"Campaign", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Spieler auswählen",
+			"Rundenanzahl", "Neues Spiel",
+			"oder Spiel laden", "Spiel laden",
+			"Kampagne", "Starten",
+			"Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Výber hráčov",
+			"Kolá", "Názov novej hry",
+			"alebo načítať hru", "Načítať hru",
+			"Kampaň", "OK",
+			"Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Выберите игроков",
+			"Кол-во раундов", "Имя для игры",
+			"или имя прошлой игры", "Загрузить игру",
+			"Кампания", "OK",
+			"Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Select Players",
+			"Rounds", "New Game Name",
+			"or Load Game", "Load Game",
+			"Campaign", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Select Players",
+			"Rounds", "New Game Name",
+			"or Load Game", "Load Game",
+			"Campaign", "Okay",
+			"Back", 0x0
+		}
+	}, {
+		/* -------------------- *
+		 * --- PLAYER       --- *
+		 * -------------------- *
+		 * Note: The title says "New Player", but this class is used for the
+		 * player editing, too. There the title is substituted by the player
+		 * name.
+		 * Further the "New Player" screen itself does not display the
+		 * "Delete This Player" option.
+		 */
+		{
+		/* === EL_ENGLISH === */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Neuer Spieler",
+			"Name", "Farbe",
+			"Typ", "Team",
+			"Erzeuge Konfig", "Gespielt",
+			"Gewonnen", "Panzertyp",
+			"Diesen Spieler Löschen", "Anlegen",
+			"Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+		/* ===== Needs to be translated ===== */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "OK",
+			"Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+		/* ===== Needs to be translated ===== */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "OK",
+			"Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "Okay",
+			"Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"New Player",
+			"Name", "Colour",
+			"Type", "Team",
+			"Generate Pref", "Played",
+			"Won", "Tank Type",
+			"Delete This Player", "Okay",
+			"Back", 0x0
+		}
+	}, {
+		/* -------------------- *
+		 * --- PLAYERS      --- *
+		 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Players",
+			"Create New", "Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+			"Players",
+			"Create New", "Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+			"Players",
+			"Create New", "Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Spieler",
+			"Neuer Spieler", "Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+		/* ===== Needs to be translated ===== */
+			"Players",
+			"Create New", "Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+		/* ===== Needs to be translated ===== */
+			"Players",
+			"Create New", "Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Players",
+			"Create New", "Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Players",
+			"Create New", "Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- RESET        --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+			"Optionen zurücksetzen?",
+			"Zurücksetzen", "Abbruch", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+		/* ===== Needs to be translated ===== */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+		/* ===== Needs to be translated ===== */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Reset Options?",
+			"Reset", "Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- SOUND        --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Sound",
+			"All Sound", "Sound Driver",
+			"Music", "Volume Factor",
+			"Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Som",
+			"Efeitos de Som", "Sistema de Som",
+			"Música", "Volume Factor",
+			"Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+			"Sound",
+			"Effets Sonores", "Système de Son",
+			"Musique", "Volume Factor",
+			"Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Sounds",
+			"Alle Sounds", "Sound Treiber",
+			"Musik", "Lautstärkefaktor",
+			"Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Zvuk",
+			"Všetky zvuky", "Ovládač zvuku",
+			"Hudba", "Volume Factor",
+			"Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+		/* ===== Needs to be translated ===== */
+			"Sound",
+			"All Sound", "Sound Driver",
+			"Music", "Volume Factor",
+			"Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Sound",
+			"All Sound", "Sound Driver",
+			"Music", "Volume Factor",
+			"Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Sound",
+			"All Sound", "Sound Driver",
+			"Music", "Volume Factor",
+			"Back", 0x0
+		}
+	}, {
+	/* -------------------- *
+	 * --- WEATHER      --- *
+	 * -------------------- */
+		{
+		/* === EL_ENGLISH === */
+			"Weather",
+			"Meteor Showers", "Lightning",
+			"Falling Dirt", "Laser Satellite",
+			"Fog", "Max Wind Strength",
+			"Wind Variation", "Back", 0x0
+		}, {
+		/* ===	EL_PORTUGUESE === */
+			"Condições Meteorológicas",
+			"Chuvas de Meteoro", "Relâmpagos",
+			"Sujeira de queda", "Satélite do Laser",
+			"Neblina", "Velocidade Max do Vento",
+			"Variação do Vento", "Back", 0x0
+		}, {
+		/* ===	EL_FRENCH === */
+			"Météo",
+			"Orages de météorites", "Éclairs",
+			"Saleté en chute", "Satellites Laser",
+			"Brouillard", "Force maxi du vent",
+			"Variation du vent", "Back", 0x0
+		}, {
+		/* ===	EL_GERMAN === */
+			"Wetter",
+			"Meteoritenregen", "Gewitter",
+			"Schmutzregen", "Lasersatellit",
+			"Nebel", "Max Windstärke",
+			"Windveränderung", "Zurück", 0x0
+		}, {
+		/* ===	EL_SLOVAK === */
+			"Počasie",
+			"Dážď meteorov", "Blesky",
+			"Padajúca zem", "Laserový satelit",
+			"Hmla", "Maximálna sila vetra",
+			"Zmena vetra", "Späť", 0x0
+		}, {
+		/* ===	EL_RUSSIAN === */
+			"Погода",
+			"Метеоритный дождь", "Молнии",
+			"Падающая грязь", "Удары со спутника",
+			"Туман", "Макс. сила ветра",
+			"Изменения силы ветра", "Назад", 0x0
+		}, {
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+			"Weather",
+			"Meteor Showers", "Lightning",
+			"Falling Dirt", "Laser Satellite",
+			"Fog", "Max Wind Strength",
+			"Wind Variation", "Back", 0x0
+		}, {
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+			"Weather",
+			"Meteor Showers", "Lightning",
+			"Falling Dirt", "Laser Satellite",
+			"Fog", "Max Wind Strength",
+			"Wind Variation", "Back", 0x0
+		}
+	}
+};
+
+
+/** @brief string array for the option text class content
+  *
+  * The ordering, although it looks a bit overwhelming here, is quite simple.
+  * The first index is the text class, the second is the language.
+  *
+  * With this both translation and adding new content is fairly easy. Just
+  * copy a block (after adding new enum entries at the proper places in
+  * optiontypes.h) and edit to the new content.
+  *
+  * All text arrays end with a zero 0x0 entry. It is therefore not needed to
+  * hard code any option value sizes.
+**/
+const char* const
+OptionClassText[TC_TEXTCLASS_COUNT][EL_LANGUAGE_COUNT][maxEntriesPerClass] = {
+	{
+	/* -------------------- *
+	 * --- TC_COLOUR   --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Regular", "Crispy", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+		{ "Regular", "Crispy", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Régulier", "Croustillant", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Normal", "Kontrastreich", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Normálna", "Svieža", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Обычная", "Четкая", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Regular", "Crispy", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Regular", "Crispy", 0x0 }
+
+	}, {
+	/* --------------------- *
+	 * --- TC_LANDSLIDE --- *
+	 * --------------------- */
+		/* === EL_ENGLISH === */
+		{ "None", "Tank Only", "Instant", "Gravity", "Cartoon", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Nenhum", "Tanque Somente", "Instantâneo", "Gravidade", "Cartoon", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Aucun", "Réservoir Seulement", "Instantané", "Gravité", "Dessin animé", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Keine", "Nur Panzer", "Sofort", "Schwerkraft", "Cartoon", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Žiaden", "Iba tank", "Okamžitý", "Gravitácia", "Kresl.film", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Выкл.", "Только танки", "Сразу же", "По умолчанию", "Как в мультиках", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "None", "Tank Only", "Instant", "Gravity", "Cartoon", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "None", "Tank Only", "Instant", "Gravity", "Cartoon", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_LANDTYPE --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Random", "Canyons", "Mountains", "Valleys", "Hills", "Foothills", "Plains", "None", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Aleatório", "Canyons", "Montanhas", "Vales", "Colinas", "Morros", "Planos", "Nenhum", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Aléatoire", "Canyons", "Montagnes", "Vallées", "Collines", "Contreforts", "Plaines", "Aucun", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Zufällig", "Canyons", "Berge", "Täler", "Hügel", "Flache Hügel", "Ebene", "Nichts", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Náhodná", "Kaňony", "Hory", "Údolia", "Kopce", "Úpätia", "Nížiny", "Žiadna", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Случайный", "Каньоны", "Горы", "Возвышенность", "Холмы", "Предгорья", "Равнины", "Выкл.", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Random", "Canyons", "Mountains", "Valleys", "Hills", "Foothills", "Plains", "None", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Random", "Canyons", "Mountains", "Valleys", "Hills", "Foothills", "Plains", "None", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_LANGUAGE --- *
+	 * -------------------- */
+
+		/* === EL_ENGLISH === */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Anglicky", "Portugalsky", "Francúzsky", "Nemecky", "Slovensky", "Rusky", "Spanish", "Italian", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Русский", "Spanish", "Italian", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "English", "Português", "Français", "Deutsch", "Slovak", "Russian", "Spanish", "Italian", 0x0 }
+
+	}, {
+	/* --------------------- *
+	 * --- TC_LIGHTNING --- *
+	 * --------------------- */
+		/* === EL_ENGLISH === */
+		{ "Off", "Weak", "Energetic", "Violent", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Desligado", "Fraco", "Energético", "Violento", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Aucun", "Faible", "Energique", "Violent", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Aus", "Schwach", "Energetisch", "Brutal", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vypnuté", "Slabé", "Energetické", "Kruté", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Нет", "Слабые", "Сильные", "Мощные", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Weak", "Energetic", "Violent", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Weak", "Energetic", "Violent", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_METEOR   --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Off", "Light", "Heavy", "Lethal", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Desligado", "Fraco", "Forte", "Letal", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Light", "Heavy", "Lethal", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Aus", "Leicht", "Schwer", "Tödlich", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vypnuté", "Ľahké", "Ťažké", "Smrteľné", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Нет", "Слабый", "Сильный", "Смертельный", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Light", "Heavy", "Lethal", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Light", "Heavy", "Lethal", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_MOUSE    --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Custom", "Default", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Personalizado", "Padrão", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Pesonnel", "Défaut", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Angepasst", "Standard", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vlastné", "Východzie", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Собственный", "По умолчанию", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Custom", "Default", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Custom", "Default", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_OFFON    --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Off", "On", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Desligado", "Ligado", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Non", "Oui", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Aus", "An", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vypnuté", "Zapnuté", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Выкл.", "Вкл.", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "On", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "On", 0x0 }
+
+	}, {
+	/* ----------------------- *
+	 * --- TC_OFFONRANDOM --- *
+	 * ----------------------- */
+		/* === EL_ENGLISH === */
+		{ "Off", "On", "Random", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Desligado", "Ligado", "Aleatório", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Non", "Oui", "Hasard", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Aus", "An", "Zufällig", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vypnuté", "Zapnuté", "Náhodný", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Выкл.", "Вкл.", "Случайно", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "On", "Random", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "On", "Random", 0x0 }
+
+	}, {
+	/* ----------------------- *
+	 * --- TC_PLAYERPREF  --- *
+	 * ----------------------- */
+		/* === EL_ENGLISH === */
+		{ "Per Game", "Only Once", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+		{ "Per Game", "Only Once", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+		{ "Per Game", "Only Once", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Pro Spiel", "Nur einmal", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Na hru", "Iba raz", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Каждую игру заново", "Только один раз", 0x0 },
+		/* ===	EL_SPANISH === */
+		{ "Por Juego", "Solo una vez", 0x0 },
+		/* ===	EL_ITALIAN === */
+		{ "Per Gioco", "Only Once", 0x0 },
+
+	} , {
+	/* ----------------------- *
+	 * --- TC_PLAYERTEAM  --- *
+	 * ----------------------- */
+		/* === EL_ENGLISH === */
+		{ "Sith", "Neutral", "Jedi", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+		{ "Sith", "Neutral", "Jedi", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+		{ "Sith", "Neutral", "Jedi", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Sith", "Neutral", "Jedi", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Sith", "Neutrálny", "Jedi", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Ситх", "Нейтральный", "Джедай", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Sith", "Neutral", "Jedi", 0x0 },
+		/* ===	EL_ITALIAN === */
+		{ "Sith", "Neutrale", "Jedi", 0x0 },
+
+	} , {
+	/* ----------------------- *
+	 * --- TC_PLAYERTYPE  --- *
+	 * ----------------------- */
+		/* === EL_ENGLISH === */
+		{ "Human", "Useless", "Guesser", "Range", "Targetter", "Deadly", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+		{ "Human", "Useless", "Guesser", "Range", "Targetter", "Deadly", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+		{ "Human", "Useless", "Guesser", "Range", "Targetter", "Deadly", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Mensch", "Nutzlos", "Ratlos", "Schütze", "Scharfschütze", "Tödlich", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Človek", "Nepoužiteľný", "Ten, čo háda", "Ten, čo hľadá správnu silu", "Ten, čo mieri", "Ten, čo prináša smrť", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Человек", "Ноль", "Слабый ИИ", "Средний ИИ", "Сильный ИИ", "Терминатор", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Humano", "Inservible", "Guesser", "Rango", "Targetter", "Mortal", 0x0 },
+		/* ===	EL_ITALIAN === */
+		{ "Umano", "Sottodotato", "Mediocre", "Medio", "Elevato", "Mortale", 0x0 },
+
+	}, {
+	/* --------------------- *
+	 * --- TC_SATELLITE --- *
+	 * --------------------- */
+		/* === EL_ENGLISH === */
+		{ "Off", "Weak", "Strong", "Super", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Desligado", "Fraco", "Forte", "Super", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Aucun", "Faible", "Fort", "Super", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Aus", "Schwach", "Stark", "Super", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vypnutý", "Slabý", "Silný", "Super", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Нет", "Слабые", "Сильные", "Супер!!", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Weak", "Strong", "Super", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Weak", "Strong", "Super", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_SKIPTYPE --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Off", "Humans Dead", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Wrong translation ? ===== */
+		{ "Desligado", "Ligado", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Wrong translation ? ===== */
+		{ "Non", "Oui", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Aus", "Menschen Tot", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vypnuté", "Smrť ľudí", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		/* ===== Wrong translation ? ===== */
+		{ "Выкл.", "Вкл.", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Humans Dead", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Off", "Humans Dead", 0x0 }
+
+	}, {
+	/* ----------------------- *
+	 * --- TC_SOUNDDRIVER --- *
+	 * ----------------------- */
+		/* === EL_ENGLISH === */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Automatisch", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_SLOVAK === */
+		/* ===== Needs to be translated ===== */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Auto Detect", "OSS", "ESD", "ARTS", "ALSA", "JACK", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_TANKTYPE --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Normal", "Classic", "Big Grey", "T34", "Heavy", "Future", "UFO", "Spider", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		/* ===== Needs to be translated ===== */
+		{ "Normal", "Classic", "Big Grey", "T34", "Heavy", "Future", "UFO", "Spider", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_FRENCH === */
+		/* ===== Needs to be translated ===== */
+		{ "Normal", "Classic", "Big Grey", "T34", "Heavy", "Future", "UFO", "Spider", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Normal", "Klassisch", "Der Große Graue", "T34", "Schwergewicht", "Futuristisch", "UFO", "Spinne", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Bežný", "Klasický", "Veľký šedý", "T34", "Ťažký", "Futuristický", "UFO", "Spider", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Обычный", "В старом стиле", "Большой Серый Танк", "Т-34", "Heavy", "Future", "UFO", "Spider", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_SPANISH === */
+		{ "Normal", "Clasico", "Big Grey", "T34", "Pesado", "Futuro", "UFO", "Araña", "Big Foot", "Mini", 0x0 },
+		/* ===	EL_ITALIAN === */
+		{ "Normale", "Classico", "Big Grey", "T34", "Pesante", "Futuro", "UFO", "Spider", "Big Foot", "Mini", 0x0 },
+
+	}, {
+	/* -------------------- *
+	 * --- TC_TURNTYPE --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "High+", "Low+", "Random", "Simul", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Melhores+", "Piores+", "Aleatório", "Simular", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Haut", "Bas", "Aléatoire", "Similaire", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Hoch+", "Niedrig+", "Zufällig", "Simul", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Vysoký+", "Nízky+", "Náhodný", "Simul", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Сильные +", "Слабые +", "Случайно", "Все сразу", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "High+", "Low+", "Random", "Simul", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "High+", "Low+", "Random", "Simul", 0x0 }
+
+	}, {
+	/* -------------------- *
+	 * --- TC_WALLTYPE --- *
+	 * -------------------- */
+		/* === EL_ENGLISH === */
+		{ "Rubber", "Steel", "Spring", "Wrap", "Random", 0x0 },
+		/* ===	EL_PORTUGUESE === */
+		{ "Elástico", "Aço", "Mola", "Envoltório", "Aleatório", 0x0 },
+		/* ===	EL_FRENCH === */
+		{ "Elastique", "Acier", "Mou", "Enveloppe", "Aléatoire", 0x0 },
+		/* ===	EL_GERMAN === */
+		{ "Gummi", "Stahl", "Federnd", "Verbunden", "Zufällig", 0x0 },
+		/* ===	EL_SLOVAK === */
+		{ "Guma", "Oceľ", "Pružina", "Prikrývka", "Náhodný", 0x0 },
+		/* ===	EL_RUSSIAN === */
+		{ "Резиновые", "Непробиваемые", "Пружинящие", "Бесконечность", "Случайные", 0x0 },
+		/* ===	EL_SPANISH === */
+		/* ===== Needs to be translated ===== */
+		{ "Rubber", "Steel", "Spring", "Wrap", "Random", 0x0 },
+		/* ===	EL_ITALIAN === */
+		/* ===== Needs to be translated ===== */
+		{ "Rubber", "Steel", "Spring", "Wrap", "Random", 0x0 }
+
+	}
+}; // End of MenuClassText
+
+
+#endif // ATANKS_SRC_OPTIONCONTENT_H_INCLUDED
+
diff --git a/src/optionitem.h b/src/optionitem.h
new file mode 100644
index 0000000..83e4c0d
--- /dev/null
+++ b/src/optionitem.h
@@ -0,0 +1,567 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONITEM_H_INCLUDED
+#define ATANKS_SRC_OPTIONITEM_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "optionitembase.h"
+
+/** @file optionitem.h
+  * @brief declaration of the option entry template
+**/
+
+
+/** @class OptionItem
+  * @brief abstract one option menu entry
+  *
+  * One option menu entry is what the user interacts with. Each option menu
+  * entry can handle exactly one target value, or none if none is needed.
+  * Target values can be manipulated directly or with the help of an action
+  * function set via function pointer upon creation.
+  * Either a target value or an action function must be set unless the entry
+  * is a sub menu.
+  * In the latter case activating the option displays the sub menu and hands
+  * over control to its menu handler.
+  *
+  * The setup is fairly easy, just use the appropriate setData() method. The
+  * template itself has only two other methods: activate() and display(). See
+  * OptionItemBase for more public methods.
+  *
+  * When using the empty ctor, remember to call it with an explicit type.
+  * To be usable with the new-operator this class has an empty ctor which
+  * leaves all members with neutral default values. To become a usable instance
+  * the method setData() must be called with the desired values.
+  *
+  *
+  * One <B>BIG</B> fat warning, though: Do not use ET_TEXT with anything other
+  * than a char* target, and only allow the usage of OptionItem::activate()
+  * if it is not full, yet!\n
+  * Background: Allowing other types would make the internals insanely
+  * complicated, and all string options that can be manipulated with the option
+  * menu are char* strings (or arrays) already. Do not change this.
+  *
+  * Why a template?\n
+  * With a template the option menu becomes type agnostic. This allowed to
+  * correct all global, environment and player data values with inappropriate
+  * types to proper types without the need to adapt the option menu.
+  *
+  * Template parameters:\n
+  * While tgt_T defines the target type, opt_T is used for the manipulating
+  * values like minimum, maximum, increment/decrement value and action function
+  * typing. This is done to allow a more intuitive usage.\n
+  * As an example, if the target is a double, minimum, maximum and increment
+  * values can be set to -1.0, 1.0 and 0.1 without the need for a postfix 'L'.
+**/
+template<typename tgt_T, typename opt_T = int32_t>
+class OptionItem : public OptionItemBase
+{
+public:
+
+	/* -------------------------------------------
+	 * --- Public constructors and destructors ---
+	 * -------------------------------------------
+	 */
+
+	/** @brief default ctor, no special functions or variants.
+	  *
+	  * This simplest ctor defines an option that can only be ET_TEXT.
+	  *
+	  * @param[in,out] target_ Pointer to the value to handle.
+	  * @param[in] max_ Maximum length for ET_TEXT options.
+	  * @param[in] color_ Color of the text to display.
+	  * @param[in] type_ Type of the option.
+	  * @param[in] title_ The title of the option to display.
+	  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+	  * @param[in] format_ Format string to use when displaying the ET_TEXT target.
+	  * @param[in] top_ Top position of the display area.
+	  * @param[in] left_ Left position of the display area.
+	  * @param[in] width_ Width of the display area.
+	  * @param[in] height_ Height of the display area.
+	  * @param[in] padding_ Padding of the title and buttons to the display area.
+	**/
+	OptionItem(tgt_T*        target_,
+	           opt_T         max_,
+	           int32_t       color_,
+	           eEntryType    type_,
+	           const char*   title_,
+	           int32_t       titleIdx_,
+	           const char*   format_,
+	           int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+	           int32_t padding_) :
+		OptionItemBase(type_, title_, titleIdx_,
+					nullptr, color_, TC_NONE, format_,
+					top_, left_, width_, height_, padding_, 0),
+		maxVal(max_),
+		target(target_)
+	{
+		// The value of max_ determines whether this
+		// is read only or not. If it is set, it is writable.
+		// The default is true, so only if it maxVal is 0, something has to be done.
+		if (maxVal)
+			read_only = false;
+	}
+
+
+	/** @brief ctor for ET_VALUE with optional display function.
+	  *
+	  * This ctor creates an ET_VALUE, a different type can not be set.
+	  *
+	  * @param[in,out] target_ Pointer to the value to handle.
+	  * @param[in] title_ The title of the option to display.
+	  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+	  * @param[in] text_ Array of texts to display.
+	  * @param[in] color_ Color of the text to display.
+	  * @param[in] class_ The text class of @a text_.
+	  * @param[in] min_ Minimum value target can become.
+	  * @param[in] max_ Maximum value target can become.
+	  * @param[in] decinc_ Value target is changed on each action.
+	  * @param[in] format_ Format string to use when displaying the target.
+	  * @param[in] top_ Top position of the display area.
+	  * @param[in] left_ Left position of the display area.
+	  * @param[in] width_ Width of the display area.
+	  * @param[in] height_ Height of the display area.
+	  * @param[in] padding_ Padding of the title and buttons to the display area.
+	  * @param[in] display_ optional display function to use.
+	**/
+	explicit
+	OptionItem(tgt_T*        target_,
+				const char*  title_,
+				int32_t      titleIdx_,
+				const char** text_,
+				int32_t      color_,
+				eTextClass   class_,
+				opt_T min_,  opt_T max_, opt_T decinc_,
+				const char*  format_,
+				int32_t top_, int32_t left_, int32_t width_, int32_t height_, int32_t padding_,
+				bool (*display_)(tgt_T* target, int32_t x, int32_t y)) :
+		OptionItemBase(ET_VALUE, title_, titleIdx_,
+						text_, color_, class_, format_,
+						top_, left_, width_, height_, padding_, 0),
+		displayFunc(display_),
+		decinc(decinc_),
+		maxVal(max_),
+		minVal(min_),
+		target(target_)
+	{
+		// The target must be set, this variant does not allow
+		// an action function:
+		assert(target && "A target must be set with ET_VALUE");
+
+		// maxVal must be larger than minVal, otherwise they are swapped
+		if (maxVal < minVal) {
+			opt_T tmp = minVal;
+			minVal = maxVal;
+			maxVal = tmp;
+		}
+
+		// If this is a text rotator, entryNum must be set to *target:
+		entryNum = static_cast<int32_t>(*target);
+
+		// Either format, texts or display must be set
+		assert( (format || texts || displayFunc || (TC_NONE == class_))
+			&& "Either format, texts or display must be set with ET_VALUE");
+	}
+
+
+	/** @brief free ctor with action function.
+	  *
+	  * Here the handling is done by an action function. A display
+	  * function can be optionally set, too.
+	  *
+	  * If this is an ET_ACTION type, a display function must be set.
+	  * For ET_BUTTON, an action function is mandatory.
+	  *
+	  * @param[in,out] target_ Pointer to the value to handle.
+	  * @param[in,out] action_ Pointer to the action function handling the target.
+	  * @param[in] type_ Type of the option.
+	  * @param[in] title_ The title of the option to display.
+	  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+	  * @param[in] text_ Array of texts to display. Only needed with ET_VALUE.
+	  * @param[in] color_ Color of the text to display.
+	  * @param[in] class_ The text class of @a text.
+	  * @param[in] format_ Format string to use when displaying the target.
+	  * @param[in] top_ Top position of the display area.
+	  * @param[in] left_ Left position of the display area.
+	  * @param[in] width_ Width of the display area.
+	  * @param[in] height_ Height of the display area.
+	  * @param[in] padding_ Padding of the title and buttons to the display area.
+	  * @param[in] display_ optional display function to use.
+	**/
+	OptionItem(tgt_T*        target_,
+	           int32_t     (*action_)(tgt_T* target, int32_t val),
+	           eEntryType    type_,
+	           const char*   title_,
+	           int32_t       titleIdx_,
+	           const char**  text_,
+	           int32_t       color_,
+	           eTextClass    class_,
+	           opt_T min_, opt_T max_, opt_T decinc_,
+	           const char*   format_,
+	           int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+	           int32_t padding_,
+	           bool        (*display_)(tgt_T* target, int32_t x, int32_t y)) :
+		OptionItemBase(type_, title_, titleIdx_,
+						text_, color_, class_, format_,
+						top_, left_, width_, height_, padding_, 0),
+		actionFunc(action_),
+		displayFunc(display_),
+		decinc(decinc_),
+		maxVal(max_),
+		minVal(min_),
+		target(target_)
+	{
+		// Either action or target must be set
+		assert ( (actionFunc || target)
+			&& "Either action or target must be set");
+
+		// If this is an ET_ACTION, both action and display
+		// functions must be set:
+		assert ( ( (ET_ACTION != type) || (actionFunc && displayFunc) )
+			  && "ET_ACTION needs both display and action function!" );
+
+		// An ET_MENU must have a display function set. To be more concrete,
+		// it must be OptionMenu->displaySub(). (Although this isn't checked.)
+		assert ( ( (ET_MENU != type) || displayFunc )
+			  && "ET_MENU must have a display function (OptionMenu->displaySub()) set!");
+
+
+		// If this is ET_VALUE and no action function is set, the same
+		// limitations as with the ET_VALUE ctor are present.
+		if (ET_VALUE == type) {
+			if (maxVal < minVal) {
+				opt_T tmp = minVal;
+				minVal = maxVal;
+				maxVal = tmp;
+			}
+
+			// If this is a text rotator, entryNum must be set to *target:
+			entryNum = static_cast<int32_t>(*target);
+
+			// Either format, texts or display must be set
+			assert( (format || texts || displayFunc || (TC_NONE == class_))
+				&& "Either format, texts or display must be set with ET_VALUE");
+		}
+	}
+
+
+	/** @brief special ctor to define a button.
+	  *
+	  * Here the handling can be done by an action function, otherwise a key
+	  * code associated with the button is returned on activation.
+	  *
+	  * Buttons have no target, so set a dummy type when calling the ctor.
+	  *
+	  * @param[in] keyCode_ The key code to return when no action function is set.
+	  * @param[in,out] target_ Pointer to the value to handle.
+	  * @param[in,out] action_ Pointer to the action function handling the button click.
+	  * @param[in] title_ The title of the option to display.
+	  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+	  * @param[in] button_ Pointer to the button to use, it must be pre-created.
+	  * @param[in] top_ Top position of the display area.
+	  * @param[in] left_ Left position of the display area.
+	  * @param[in] width_ Width of the display area.
+	  * @param[in] height_ Height of the display area.
+	  * @param[in] padding_ Padding of the title and buttons to the display area.
+	**/
+	OptionItem(int32_t      keyCode_,
+	           tgt_T*       target_,
+	           int32_t    (*action_)(tgt_T* target, int32_t val),
+	           const char*  title_,
+	           int32_t      titleIdx_,
+	           BUTTON*      button_,
+	           int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+	           int32_t padding_) :
+		OptionItemBase(ET_BUTTON, title_, titleIdx_,
+						nullptr, BLACK, TC_NONE, nullptr,
+						top_, left_, width_, height_, padding_, 0),
+		actionFunc(action_),
+		target(target_)
+	{
+		// Either action or keyCode must be set
+		assert ( (actionFunc || keyCode_)
+			&& "Either action_ or keyCode_ must be set");
+
+		if (keyCode_) this->keyCode = keyCode_;
+		if (button_) {
+			this->button = button_;
+			this->button->getLocation(this->left, this->top, this->width, this->height);
+		}
+	}
+
+
+	/// @brief default dtor only setting nullptr values. No further action needed.
+	~OptionItem()
+	{
+		actionFunc  = nullptr;
+		displayFunc = nullptr;
+		target      = nullptr;
+	}
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	/** @brief activate the option handling
+	  *
+	  * This activates whatever the option is configured for.
+	  *
+	  * If an action function is set, it is simply called. The parameter is
+	  * then ignored. If this is an ET_VALUE and no action function is set,
+	  * the configured increment/decrement value is applied according to @a val.
+	  * If this is an ET_TEXT option, the appropriate ways to get the target
+	  * value are used.
+	  *
+	  * @param[in] val Used for ET_VALUE: <0 = decrement, >0 = increment.
+	  * @param[in] ignored (see OptionItemColour)
+	  * @param[in] ignored (see OptionItemColour)
+	  * @param[in] k The latest key press to use on an ET_TEXT.
+	  * @return normally 0, but ET_BUTTON and ET_MENU can return key_codes
+	  * assigned with exit buttons.
+	**/
+	int32_t activate(int32_t val, int32_t, int32_t, int32_t k)
+	{
+		int32_t result = 0;
+
+		if (actionFunc) {
+			result = actionFunc(target, val);
+
+			// Here it is important that the action function does the right
+			// thing with the target if this is an ET_VALUE and val<>0
+			if ((ET_VALUE == type) && val && texts)
+				entryNum = static_cast<int32_t>(*target);
+
+		} else {
+			// Here a target must be set as there is no action function.
+			if ((ET_BUTTON != type) && !target && !actionFunc) {
+				cerr << "\n" << __FUNCTION__ << ": Illegal setup!" << endl;
+				cerr << "A target value MUST be set with ET_";
+				cerr << ( ET_COLOR  == type ? "COLOR"
+						: ET_TEXT   == type ? "TEXT"
+						: ET_TOGGLE == type ? "TOGGLE"
+						: ET_VALUE  == type ? "VALUE" : "UNKNOWN") << endl;
+				return false;
+			}
+
+			// Every type has its own base class activation function,
+			// so a simple switch will do.
+			switch(type) {
+				case ET_BUTTON:
+					// Can trigger end events
+					result = this->keyCode;
+					break;
+				case ET_MENU:
+					result = this->activateMenu(target);
+					break;
+				case ET_TEXT:
+					if (!read_only)
+						this->activateText(target, k);
+					break;
+				case ET_TOGGLE:
+					this->activateToggle(target);
+					break;
+				case ET_VALUE:
+					this->activateValue(val);
+					break;
+				case ET_COLOR:
+				case ET_NONE:
+				case ET_ACTION:
+				default:
+					cerr << "\n" << __FUNCTION__ << ": Illegal setup!" << endl;
+					cerr << " No action function set when it is needed!" << endl;
+					return false;
+			} // End of switch(type)
+		} // End of having no action function
+
+		// Changes are displayed at once:
+		// (unless this is ET_COLOR, it has been drawn already.)
+		if (ET_COLOR != this->type) {
+			this->clear_display(true);
+			this->display(true);
+		}
+
+		return result;
+	}
+
+
+	/// @brief return true if the target has not reached its minimum, yet
+	virtual bool canGoDown()
+	{
+		if ( (ET_VALUE == this->type) && this->format)
+			// Check format, because texts[] based options are rotated.
+			return (*target > static_cast<tgt_T>(minVal));
+		return true;
+	}
+
+
+	/// @brief return true if the target has not reached its maximum, yet
+	virtual bool canGoUp()
+	{
+		if ( (ET_VALUE == this->type) && this->format)
+			return (*target < static_cast<tgt_T>(maxVal));
+		return true;
+	}
+
+
+	/** @brief display the option content
+	  *
+	  * This method either calls the set display function or its own one.\n
+	  * ET_ACTION and ET_MENU <B>must</B> have set a display function.
+	  *
+	  * If @a show_full is set to true, the option title and possible
+	  * button(s) are displayed, too. This is useful to initially display a
+	  * menu or when switching languages.
+	  *
+	  * However, if the option is either ET_MENU or ET_ACTION, @a show_full
+	  * is ignored.
+	  *
+	  * @param[in] show_full If set to true, title and buttons are redrawn.
+	**/
+	void display(bool show_full)
+	{
+		if (displayFunc) {
+			clear_display(false);
+			displayFunc(target, left, top);
+			drawn = true;
+		} else {
+			// Every type has its own base class display function,
+			// so a simple switch will do.
+			switch(type) {
+				case ET_BUTTON:
+					this->displayButton();
+					break;
+				case ET_MENU:
+					this->displayMenu(target);
+					break;
+				case ET_TEXT:
+					this->displayText(target);
+					break;
+				case ET_TOGGLE:
+					this->displayToggle(target);
+					break;
+				case ET_VALUE:
+					this->displayValue(target);
+					break;
+				case ET_NONE:
+				case ET_ACTION:
+				case ET_COLOR:
+				default:
+					cerr << "\n" << __FUNCTION__ << ": Illegal setup!" << endl;
+					cerr << " No display function set when it is needed!" << endl;
+					return;
+			} // End of switch(type)
+		} // end of having no display function
+
+		// Show decorations if wanted: (ET_COLOR does that in displayColor())
+		if (show_full && (ET_COLOR != type))
+			this->displayDeco();
+	}
+
+
+	/// @brief return true if this is an ET_BUTTON with a key code and no action function.
+	bool isExitButton()
+	{
+		return ((ET_BUTTON == type) && (nullptr == actionFunc) && (-1 < keyCode));
+	}
+
+
+	/// @brief Quickly change (or set) the action function
+	void setAction(int32_t (*action_)(tgt_T* target, int32_t val))
+	{
+		actionFunc = action_;
+	}
+
+
+private:
+
+	/* ----------------------------------------------
+	 * --- Private methods and external functions ---
+	 * ----------------------------------------------
+	 */
+
+	int32_t (*actionFunc )(tgt_T* target, int32_t val)          = nullptr;
+	bool    (*displayFunc)(tgt_T* target, int32_t x, int32_t y) = nullptr;
+
+	/// @brief templated ET_VALUE activation handling
+	void activateValue(int32_t val)
+	{
+		// A few short-cuts that make reading the following a lot easier:
+		tgt_T t_val = static_cast<tgt_T>(val * decinc);
+		tgt_T t_max = static_cast<tgt_T>(maxVal);
+		tgt_T t_min = static_cast<tgt_T>(minVal);
+
+		if (format) {
+			// If a format is set, this is just a simple adding/substracting
+			// of decinc with a check against min/max value afterwards
+			// val == 0 is simply ignored.
+			tgt_T oldTgt = *target;
+			if (val > 0) {
+				if (*target <= (t_max - t_val) )
+					*target += t_val;
+				else
+					*target = t_max;
+			} else if (val < 0) {
+				if (*target >= (t_min - t_val) )
+					*target += t_val;
+				else
+					*target = t_min;
+			}
+			// If a maximum or minimum is reached, clear the decoration
+			if ( (oldTgt != *target)
+			  && ( (*target == t_min)
+				|| (*target == t_max) ) ) {
+				clear_display(true);
+				display(true);
+			}
+		} else if (texts) {
+			// Otherwise entryNum is used and checked against texts[]
+			if (val > 0) {
+				if(  texts[entryNum + 1]
+				  && (*target < t_max ) )
+					++entryNum;
+				else
+					entryNum = 0;
+			} else if (val < 0) {
+				if(  entryNum > 0
+				  && (*target > static_cast<tgt_T>(0) ) )
+					--entryNum;
+				else
+					entryNum = t_max;
+			}
+			*target = static_cast<tgt_T>(entryNum);
+		}
+	}
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	opt_T  decinc  = (opt_T)1; //!< Increment / decrement for ET_VALUE
+	opt_T  maxVal  = (opt_T)0; //!< Maximum value for ET_VALUE
+	opt_T  minVal  = (opt_T)0; //!< Minimum value for ET_VALUE
+	tgt_T* target  = nullptr;  //!< Target to handle
+};
+
+
+#endif // ATANKS_SRC_OPTIONITEM_H_INCLUDED
+
diff --git a/src/optionitembase.cpp b/src/optionitembase.cpp
new file mode 100644
index 0000000..f0ccada
--- /dev/null
+++ b/src/optionitembase.cpp
@@ -0,0 +1,768 @@
+#include "button.h"
+#include "menu.h"
+#include "optionitembase.h"
+#include "floattext.h"
+
+static const char menu_hint_text[]   = "-> ";
+static uint32_t   menu_hint_text_len = 0; // Set by ctor when "font" is set
+static const char select_text[]      = "* ";
+uint32_t          select_text_len    = 0; // Set by ctor when "font" is set
+
+// Note: Direct setting is not a good idea, font might be anything when
+//       the static initialization is done.
+static int32_t CURSOR_FLIP_TIME = 25; // Delays cursor flipping
+
+
+/** @brief default and only constructor
+  * @param[in] type_ Type of the option.
+  * @param[in] title_ The title of the option to display.
+  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+  * @param[in] text_ Array of texts to display.
+  * @param[in] color_ The color to use for the text, mainly useful for OT_TOGGLE.
+  * @param[in] class_ Index value of the submitted texts or TC_NONE if no texts are needed.
+  * @param[in] format_ Format string to use when displaying the ET_TEXT target.
+  * @param[in] top_ Top position of the display area.
+  * @param[in] left_ Left position of the display area.
+  * @param[in] width_ Width of the display area.
+  * @param[in] height_ Height of the display area.
+  * @param[in] padding_ Padding of the title and buttons to the display area.
+  * @param[in] show_size_ Sets the size of the show color box. (ET_COLOR only)
+**/
+OptionItemBase::OptionItemBase( eEntryType    type_,
+                                const char*   title_,
+                                int32_t       titleIdx_,
+                                const char**  text_,
+                                int32_t       color_,
+                                eTextClass    class_,
+                                const char*   format_,
+                                int32_t top_, int32_t left_,
+                                int32_t width_, int32_t height_,
+                                int32_t padding_, int32_t show_size_) :
+	color(color_),
+	format(format_),
+	height(height_),
+	left(left_),
+	padding(padding_),
+	show_size(show_size_),
+	textClass(class_),
+	texts(text_),
+	title(title_),
+	titleIdx(titleIdx_),
+	top(top_),
+	type(type_),
+	width(width_)
+{
+	// Assert the title as the most basic value
+	assert (title && "Title not set");
+	assert ((texts || (TC_NONE == textClass)) && "text_ and class_ do not fit!");
+	titleLen = text_length(font, title);
+
+    // Set static globals
+    if (0 == menu_hint_text_len)
+		menu_hint_text_len = text_length(font, menu_hint_text);
+    if (0 == select_text_len)
+		select_text_len = text_length(font, select_text);
+}
+
+
+/// @brief simple default destructor
+OptionItemBase::~OptionItemBase()
+{
+	this->remove();
+	if (this->button)
+		delete this->button;
+}
+
+
+/* =====================================
+ * === Public method implementations ===
+ * =====================================
+ */
+
+/** @brief clear the current display
+  *
+  * This method erases the current display. It should be called
+  * before changing the displayed value or the display parameters
+  * like coordinates or dimension.
+  *
+  * The methods that change those parameters or draw the display call
+  * clear_display() automatically. Please remember to call it in external
+  * display functions.
+  *
+  * Note: As ET_TOGGLE have their title being the text to display, setting
+  * @a update_full will only change the state of the decorated state and not
+  * clear any additional space.
+  */
+void OptionItemBase::clear_display(bool update_full)
+{
+	if (drawn || (update_full && decorated)) {
+		int32_t xLeft   = left;
+		int32_t xWidth  = drawn ? width : 0;
+		int32_t xTop    = top;
+		int32_t xHeight = height;
+
+		// clear decoration?
+		if (update_full) {
+
+			// First: Title if displayed left of the display area
+			if ((ET_TOGGLE != type) && (ET_BUTTON != type)) {
+				xLeft  -= titleLen + padding;
+				xWidth += titleLen + padding;
+			}
+
+			// Second: Selection text if needed
+			if (selected) {
+				if ( (ET_BUTTON == type) && this->button ) {
+					// The button box must be erased
+					xLeft   -= 3;
+					xTop    -= 3;
+					xWidth  += 6;
+					xHeight += 6;
+				} else {
+					xLeft  -= select_text_len + padding;
+					xWidth += select_text_len + padding;
+				}
+			}
+
+			// Third: The ET_VALUE wheel buttons
+			if (ET_VALUE == type)
+				xWidth += 2 * padding + 20;
+		}
+
+		rectfill (global.canvas, xLeft, xTop, xLeft + xWidth, xTop + xHeight,
+		          makecol (0,79,0));
+		global.make_update (xLeft, xTop, xWidth, xHeight);
+		drawn = false;
+		if (update_full)
+			decorated = false;
+	}
+}
+
+
+/// @brief Toggle cursor_on if this is a selected ET_TEXT
+void OptionItemBase::cursor_flip()
+{
+	if ((ET_TEXT == type) && selected && !read_only) {
+		if (--curs_clk < 1) {
+			curs_clk  = CURSOR_FLIP_TIME;
+			cursor_on = !cursor_on;
+			clear_display(false);
+		}
+	}
+}
+
+
+/// @brief get width and height at once
+/// Note: Special circumstances like padding, menu indication and
+///       ET_TOGGLE extra sizes are added.
+void OptionItemBase::getDimension (int32_t &tgt_width, int32_t &tgt_height)
+{
+	tgt_width  = width + padding;
+	tgt_height = height + padding + 2;
+
+	if (ET_COLOR == type)
+		tgt_width += padding + show_size;
+	if (ET_MENU == type)
+		tgt_width += menu_hint_text_len;
+	if (ET_TOGGLE == type) {
+		tgt_width  += 4;
+		tgt_height += 2;
+	}
+}
+
+/// @brief return currently set key code
+int32_t OptionItemBase::getKeyCode()
+{
+	return keyCode;
+}
+
+
+/// @brief return the value of the next pointer
+OptionItemBase* OptionItemBase::getNext()
+{
+	return next;
+}
+
+
+/// @brief return the value of the prev pointer
+OptionItemBase* OptionItemBase::getPrev()
+{
+	return prev;
+}
+
+
+/// @brief return text class as index value
+uint32_t OptionItemBase::getTextClass()
+{
+	return static_cast<uint32_t>(textClass);
+}
+
+
+/// @brief return the index value of the displayed title
+uint32_t OptionItemBase::getTitleIdx()
+{
+	return titleIdx;
+}
+
+
+/// @brief return the eEntryType of the entry
+eEntryType OptionItemBase::getType()
+{
+	return type;
+}
+
+
+/** @brief Insert option after @a new_prev
+  *
+  * This is a standard insert into a doubly linked list.
+  *
+  * @param[in,out] new_prev (Optional) pointer to the option that becomes the new prev.
+  */
+void OptionItemBase::insert_after(OptionItemBase* new_prev)
+{
+	if (prev || next)
+		this->remove();
+
+	prev = new_prev;
+	if (prev) {
+		next = prev->next;
+		prev->next = this;
+	}
+
+	if (next)
+		next->prev = this;
+}
+
+
+/** @brief Insert option before @a new_next
+  *
+  * This is a standard insert into a doubly linked list.
+  *
+  * @param[in,out] new_next (Optional) pointer to the option that becomes the new next.
+  */
+void OptionItemBase::insert_before(OptionItemBase* new_next)
+{
+	if (prev || next)
+		this->remove();
+
+	next = new_next;
+	if (next) {
+		prev = next->prev;
+		next->prev = this;
+	}
+
+	if (prev)
+		prev->next = this;
+}
+
+
+/** @brief return true if @a x and @a y are in this options clickable area
+  *
+  * This method returns true if @a x and @a y are with the clickable area
+  * of this option. This means the display area and optional wheel buttons
+  * if this is an ET_VALUE type.
+  *
+  * If this option is an ET_BUTTON and has a key code assigned, or if this is
+  * an ET_VALUE and the wheel buttons are hit, @a ret is set to the appropriate
+  * value.
+  *
+  * @param[in] x X coordinate to test.
+  * @param[in] y Y coordinate to test.
+  * @param[out] ret Value to set to an assigned key code or -1/+1 for ET_VALUE inc/dec click.
+  * @return true if this option is hit, false otherwise.
+**/
+bool OptionItemBase::is_click_in(int32_t x, int32_t y, int32_t &ret)
+{
+	int32_t xLeft   = left  + 1;
+	int32_t xTop    = top   + 1;
+	int32_t xRight  = xLeft + width  - 2;
+	int32_t xBottom = xTop  + height - 2;
+	bool    result  = false;
+
+	// reset ret
+	ret = 0;
+
+	// Note: No need to check anything if y is somewhere else
+	if ( (y >= xTop ) && (y <= xBottom) ) {
+		bool hasWheelresult = false;
+
+		// Check direct display area:
+		if ( (x >= xLeft) && (x <= xRight ) )
+			result = true;
+
+		// If this is an ET_VALUE, check wheel buttons
+		if (!result && (ET_VALUE == type)) {
+			int32_t up_left  = left + width + padding;
+			int32_t up_right = up_left + 10;
+			int32_t dn_left  = up_right + padding;
+			int32_t dn_right = dn_left + 10;
+
+			// Left "DOWN" button
+			if ( (x >= up_left) && (x <= up_right) ) {
+				result = true;
+				ret    = -1;
+				hasWheelresult = true;
+			}
+
+			// Right "UP" button
+			else if ( ( x >= dn_left) && (x <= dn_right) ) {
+				result = true;
+				ret    = 1;
+				hasWheelresult = true;
+			}
+		} // End of checking ET_VALUE wheel buttons
+
+		// Is there a return code to send?
+		if (result && !hasWheelresult)
+			// simply activate it
+			ret = KEY_ENTER;
+
+		// If a result is found, this must be updated:
+		if (result && (ET_COLOR != type))
+			this->clear_display(false);
+	} // End of y in range
+
+	return result;
+}
+
+
+/// @brief returns true if this entry is selected
+bool OptionItemBase::is_selected()
+{
+	return selected;
+}
+
+
+/** @brief move the display area
+  *
+  * This method clears the current display and then changes the position of the
+  * display area of this option.
+  *
+  * @param[in] new_left The new left position of the display area.
+  * @param[in] new_top The new top position of the display area.
+  * @param[in] do_update if set to true, the current display is cleared.
+  */
+void OptionItemBase::move(int32_t new_left, int32_t new_top, bool do_update)
+{
+	if ((new_left != left) || (new_top != top)) {
+		if (do_update)
+			clear_display(true);
+		left = new_left;
+		top  = new_top;
+	}
+}
+
+
+/// @brief return true if this option needs set texts
+bool OptionItemBase::needs_text()
+{
+	return (textClass < TC_TEXTCLASS_COUNT);
+}
+
+
+/** @brief remove this option from the list.
+  *
+  * This is a standard remove from a doubly linked list.
+  */
+void OptionItemBase::remove()
+{
+	if (next)
+		next->prev = prev;
+	if (prev)
+		prev->next = next;
+	prev = nullptr;
+	next = nullptr;
+}
+
+
+/** @brief resize the display area
+  *
+  * This method clears the current display and then changes the dimensions of
+  * the display area of this option.
+  *
+  * @param[in] new_width The new width of the display area.
+  * @param[in] new_height The new height of the display area.
+  */
+void OptionItemBase::resize(int32_t new_width, int32_t new_height)
+{
+	if ((new_width != width) || (new_height != height)) {
+		clear_display(true);
+		width  = new_width;
+		height = new_height;
+	}
+}
+
+
+/// @brief selects this entry and triggers a redraw
+void OptionItemBase::select()
+{
+	if (!selected) {
+		selected = true;
+		clear_display(true);
+	}
+}
+
+
+/** @brief Change the padding around the display area
+  *
+  * This method clears the current display and then changes the padding around
+  * the display area of this option.
+  *
+  * @param[in] new_padding The new padding value.
+  */
+void OptionItemBase::setPadding(int32_t new_padding)
+{
+	if (new_padding != padding) {
+		clear_display(true);
+		padding = new_padding;
+	}
+}
+
+
+/** @brief set a new title
+  *
+  * This method clears the current display and then changes the title. Use this
+  * to switch languages.
+  *
+  * @param[in] new_title Pointer to the new title to display
+  */
+void OptionItemBase::setTitle(const char* new_title)
+{
+	if (new_title != title) {
+		clear_display(true);
+        title    = new_title;
+		titleLen = text_length(font, title);
+		if ((ET_BUTTON == type) && button)
+			button->setText(title);
+	}
+}
+
+
+/** @brief set a new text class
+  *
+  * The only reaal purpose is to be able to add text-less
+  * options in templated add methods in the Menu class,
+  * where the OptionClassText is not available.
+  *
+  * Call this then from the compilation unit.
+**/
+void OptionItemBase::setTextClass (eTextClass new_class)
+{
+	textClass = new_class;
+}
+
+
+/** @brief set new texts array
+  *
+  * This method clears the current display without decorations and changes the
+  * used texts array. Use this to switch languages.
+  */
+void OptionItemBase::setTexts(const char** new_texts)
+{
+	if (new_texts != texts) {
+		clear_display(false);
+        texts = new_texts;
+	}
+}
+
+
+/// @brief unselects this entry and triggers a redraw
+void OptionItemBase::unselect()
+{
+	if (selected) {
+		clear_display(true);
+		selected  = false;
+		cursor_on = false;
+		curs_clk  = CURSOR_FLIP_TIME;
+	}
+}
+
+
+/* ========================================
+ * === Protected method implementations ===
+ * ========================================
+ */
+
+
+/** @brief return the outcome of a menu activation
+  * @param[in] target pointer to the menu
+  * @return Result of Menu::operator()
+**/
+int32_t OptionItemBase::activateMenu(Menu* target)
+{
+	if (target)
+		return target->operator()();
+	return 0;
+}
+
+
+/** @brief Add the result of key press @a k to @a target.
+  *
+  * Please make sure that @a has at least one byte free space excluding
+  * null character termination!
+  *
+  * @param[out] target The char array (or string) to receive the result.
+  * @param[in] k Allegro 4 raw key code, or the allegro 5 unichar field
+  */
+void OptionItemBase::activateText(char* target, int32_t k)
+{
+	if (target && (k > 0) ) {
+		int32_t oldTextLen = strlen(target);
+		char    chr        = static_cast<char>(k & 0xff);
+		textLen = oldTextLen;
+
+		if ( ((0x08 == chr) || (0x7f == chr)) && textLen )
+			target[--textLen] = 0x0;
+		else if (isprint(chr))
+			target[textLen++] = chr;
+
+		if (oldTextLen != textLen)
+			clear_display(false);
+	}
+}
+
+
+/** @brief Switch the state of @a target
+  *
+  * @param[in,out] target The target value to switch
+  */
+void OptionItemBase::activateToggle(bool* target)
+{
+	if (target) {
+		*target = !(*target);
+		clear_display(false);
+	}
+}
+
+
+/** @brief display the set button
+**/
+void OptionItemBase::displayButton()
+{
+	if (!drawn) {
+		if (this->button)
+			button->draw();
+		drawn = true;
+	}
+}
+
+
+/** @brief Display the options decoration (title plus button(s))
+  *
+  * Note: If this is ET_TOGGLE, the method will only change the decorated
+  * state to true, as the title for this type is the displayed text.
+  *
+  * @param[in] show_color Used only for the ET_COLOR show box.
+  */
+void OptionItemBase::displayDeco(int32_t show_color)
+{
+	if (!decorated) {
+		int32_t xLeft    = left;
+		int32_t xWidth   = 0;
+		int32_t xTop     = top;
+		int32_t xHeight  = height;
+		int32_t text_top = top + (height / 2) - (env.fontHeight / 2);
+		int32_t deco_top = text_top + 4;
+
+		// First: Title text
+		if (title
+		  && (ET_BUTTON != type)
+		  && (ET_MENU   != type)
+		  && (ET_TOGGLE != type)) {
+		  	int32_t tColor = (ET_COLOR == type) || textOnly ? BLACK : color;
+
+			xLeft -= titleLen + padding;
+			xWidth = titleLen + padding;
+
+			// Add a nice shadow if wanted
+		  	if (env.shadowedText)
+				textout_ex (global.canvas, font, title, xLeft + 1, text_top + 1,
+								GetShadeColor(tColor, true, PINK), -1);
+
+			textout_ex (global.canvas, font, title, xLeft, text_top, tColor, -1);
+		}
+
+		// ET_MENU has an additional "-> " displayed next to it
+		if ( (ET_MENU == type) && show_menu) {
+			xLeft  -= menu_hint_text_len + padding;
+			xWidth += menu_hint_text_len + padding;
+			textout_ex (global.canvas, font, menu_hint_text, xLeft, text_top, BLACK, -1);
+		}
+
+		// Second: Selection text if needed
+		if (selected) {
+			if ( (ET_BUTTON == type) && this->button ) {
+				// Buttons get a box drawn around them
+				xTop    -= 3;
+				xLeft   -= 3;
+				xWidth  += 6 + width; // spans over
+				xHeight += 6;
+				rect (global.canvas, xLeft, xTop, xLeft + xWidth, xTop + xHeight, WHITE);
+			} else  {
+				xLeft  -= select_text_len + padding;
+				xWidth += select_text_len + padding;
+				textout_ex (global.canvas, font, select_text, xLeft, text_top, BLACK, -1);
+			}
+		}
+
+		// If an actual width could be determined, add an update
+		// for the text region:
+		if (xWidth > 0)
+			global.make_update (xLeft, xTop, xWidth, xHeight);
+
+		// Third: The ET_VALUE wheel buttons / ET_COLOR display box
+		if (ET_VALUE == type) {
+			BITMAP* arrow = env.misc[6];
+			int32_t dn_left = left + width + padding;
+			int32_t up_left = dn_left + padding + 10;
+
+			if (this->canGoDown())
+				draw_sprite_v_flip (global.canvas, arrow, dn_left, deco_top);
+			if (this->canGoUp())
+				draw_sprite        (global.canvas, arrow, up_left, deco_top);
+
+			global.make_update (up_left, top, 10, height);
+		} else if (ET_COLOR == type) {
+			int32_t x = left + width + padding;
+
+			rect (global.canvas, x, deco_top, x + show_size, deco_top + show_size, BLACK);
+			rectfill (global.canvas,
+					x + 1, deco_top + 1,
+					x + show_size - 1, deco_top + show_size - 1,
+					show_color);
+			global.make_update (x, deco_top, show_size, show_size);
+		}
+
+		decorated = true;
+	}
+}
+
+
+/** @brief Display the @a target  menu title as text.
+  *
+  * @param[in] target pointer to the menu to display
+  */
+void OptionItemBase::displayMenu(Menu* target)
+{
+	if (title && (titleIdx > -1))
+		this->displayText(title);
+	else if (target)
+		this->displayText(target->getTitle());
+	else
+		this->displayText("Oh no! No Menu Title!");
+}
+
+
+/** @brief Display the @a target as text.
+  *
+  * @param[in] target (optional) pointer to the text to display
+  */
+void OptionItemBase::displayText(char* target)
+{
+	this->displayText(static_cast<const char*>(target));
+}
+
+
+/** @brief Display the @a target as text.
+  *
+  * @param[in] target (optional) pointer to the text to display
+  */
+void OptionItemBase::displayText(const char* target)
+{
+	if (!drawn) {
+		if (!textOnly) {
+			rect (global.canvas, left, top, left + width, top + height, BLACK);
+			rectfill (global.canvas,
+						left + 1, top + 1,
+						left + width - 1, top + height - 1,
+						WHITE);
+		}
+
+		// First display the text
+		const char* txt_p = target;
+		int32_t txt_len   = txt_p ? text_length(font, txt_p) : 0;
+
+		if (txt_p) {
+			int32_t len_available = width - 6;
+			int32_t text_top = top + (height / 2) - (env.fontHeight / 2);
+
+
+			// If this is a read/write selected ET_TEXT, it has a cursor being
+			// drawn, and thus less space to display the text:
+			if ((ET_TEXT == type) && selected && !read_only)
+				len_available -= text_length(font, "W");
+
+			// Scroll text until it fits.
+			while (txt_p && txt_p[0] && (txt_len > len_available))
+				txt_len = text_length(font, ++txt_p);
+
+			// Now print the result
+			if (txt_p && txt_p[0]) {
+
+				// With a shadow? But not in text field mode
+				if (env.shadowedText && textOnly)
+					textout_ex (global.canvas, font, txt_p, left + 4, text_top + 1,
+					            GetShadeColor(color, true, PINK), -1);
+
+				textout_ex (global.canvas, font, txt_p, left + 3, text_top,
+				            textOnly ? color : BLACK, -1);
+			}
+		}
+
+		// Then a cursor if turned on
+		if (cursor_on) {
+			int32_t offset = txt_len + left + 3;
+			rectfill (global.canvas, offset, top + 3, offset + 6, top + height - 3, BLACK);
+		}
+
+		global.make_update (left, top, width, height);
+		drawn = true;
+	}
+}
+
+
+/** @brief Display the @a target as text.
+  *
+  * @param[in] target (optional) pointer to the text to display
+  */
+void OptionItemBase::displayText(uint32_t* target)
+{
+	this->displayValue(target);
+}
+
+
+/** @brief Displays a toggle option in either black on white or vice versa
+  *
+  * Note: The title is actually the displayed text.
+  *
+  * @param[in] target (optional) pointer to the toggle option.
+  * to be set by OptionItemPlayer only.
+  */
+void OptionItemBase::displayToggle(bool* target)
+{
+	if (!drawn) {
+		int32_t fg_color = color;
+		int32_t bg_color = BLACK;
+		int32_t sh_color = DARK_GREY;
+		int32_t x_radius = (titleLen / 1.8) + padding + 5;
+		int32_t y_radius = height / 2;
+		int32_t xLeft    = left + (width / 2);
+		int32_t xTop     = top  + (height / 2);
+		int32_t txtLeft  = xLeft - (titleLen / 2) + 1;
+
+		// Swap if activated
+		if (target && *target) {
+			fg_color = BLACK;
+			bg_color = color;
+			sh_color = GREY;
+		}
+
+		// To make this nicer looking, a shade color is generated for an outline:
+		ellipsefill (global.canvas, xLeft, xTop, x_radius, y_radius, bg_color);
+		ellipse     (global.canvas, xLeft, xTop, x_radius, y_radius, sh_color);
+		textout_ex(global.canvas, font, title ? title : "", txtLeft , top + 4, fg_color, -1);
+
+		global.make_update (left, top, width, height);
+		drawn = true;
+	}
+}
diff --git a/src/optionitembase.h b/src/optionitembase.h
new file mode 100644
index 0000000..f7a76eb
--- /dev/null
+++ b/src/optionitembase.h
@@ -0,0 +1,207 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONITEMBASE_H_INCLUDED
+#define ATANKS_SRC_OPTIONITEMBASE_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "environment.h"
+#include "globaldata.h"
+#include "optiontypes.h"
+
+#include <cassert>
+#include <string>
+
+
+/** @file optionitembase.h
+  * @brief declaration of the option item base class
+**/
+
+extern uint32_t select_text_len; // needed for the item distribution
+
+// Forward BUTTON if it isn't known, yet:
+#ifndef BUTTON_HEADER_
+class BUTTON;
+#endif // BUTTON_HEADER_
+
+// Forward Menu if it isn't known, yet:
+#ifndef MENU_CLASS_DECLARES
+class Menu;
+#endif // MENU_CLASS_DECLARES
+
+
+/** @class OptionItemBase
+  * @brief Common base class for all option items.
+  *
+  * This base class holds all common values of the option item represented and
+  * is used to order option items as a doubly linked list.
+  *
+  * The class has several public methods to change the inner state, like moving
+  * or resizing the display area, changing the title, switching the text content
+  * array and so on.
+**/
+class OptionItemBase
+{
+public:
+
+	/* ------------------------------
+	 * --- Public ctors and dtors ---
+	 * ------------------------------
+	 */
+
+	explicit OptionItemBase(
+				eEntryType    type_,
+				const char*   title_,
+				int32_t       titleIdx_,
+				const char**  text_,
+				int32_t       color_,
+				eTextClass    class_,
+				const char*   format_,
+				int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+				int32_t padding_, int32_t show_size_);
+
+	virtual ~OptionItemBase();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void             clear_display(bool update_full);
+	void             cursor_flip  ();
+	void             getDimension (int32_t &tgt_width, int32_t &tgt_height);
+	int32_t          getKeyCode   ();
+	OptionItemBase*  getNext      ();
+	OptionItemBase*  getPrev      ();
+	uint32_t         getTextClass ();
+	uint32_t         getTitleIdx  ();
+	eEntryType       getType      ();
+	void             insert_after (OptionItemBase* new_prev);
+	void             insert_before(OptionItemBase* new_next);
+	bool             is_click_in  (int32_t x, int32_t y, int32_t &ret);
+	bool             is_selected  ();
+	void             move         (int32_t new_left, int32_t new_top, bool do_update);
+	bool             needs_text   ();
+	void             remove       ();
+	void             resize       (int32_t new_width, int32_t new_height);
+	void             select       ();
+	void             setPadding   (int32_t new_padding);
+	void             setTitle     (const char*  new_title);
+	void             setTextClass (eTextClass new_class);
+	void             setTexts     (const char** new_texts);
+	void             unselect     ();
+
+	// virtuals to be implemented by the deriving template
+	virtual int32_t activate    (int32_t val, int32_t x, int32_t y, int32_t k) =0;
+	virtual bool    canGoDown   ()               =0;
+	virtual bool    canGoUp     ()               =0;
+	virtual void    display     (bool show_full) =0;
+	virtual bool    isExitButton()               =0;
+
+
+protected:
+
+	/* -------------------------
+	 * --- Protected methods ---
+	 * -------------------------
+	 */
+	int32_t activateMenu(Menu* target);
+	void    activateText(char* target, int32_t k);
+	void    activateToggle(bool* target);
+	void    displayButton();
+	void    displayDeco(int32_t show_color = BLACK);
+	void    displayMenu(Menu* target);
+	void    displayText(char* target);
+	void    displayText(const char* target);
+	void    displayText(uint32_t* target);
+	void    displayToggle(bool* target);
+
+	/// @brief As OT_VALUE might be anything, it is templated on method scale.
+	template<typename tgt_T>
+	void displayValue(tgt_T* target)
+	{
+		if (format) {
+			char txt_buf[256] = { 0x0 };
+			snprintf(txt_buf, 255, format, *target);
+			textLen = strlen(txt_buf);
+			this->displayText(txt_buf);
+		} else if (texts && texts[entryNum]) {
+			textLen = strlen(texts[entryNum]);
+			this->displayText(texts[entryNum]);
+		}
+	}
+
+	// Note: The following templates only define a path for the dispatcher
+	// when checking for which method to call. If a calling path, and thus any
+	// option configuration, is invalid, they will print an error message and
+	// terminate. These templates are only instantiated by the compiler for
+	// syntax and call path checking, and thrown away being unused later.
+	// This looks like a waste, but makes the dispatching a lot less complex
+	// and more secure.
+#define EMERGENCY_OUT fprintf(stderr, \
+	"%s:%d [%s] : Illegal target type, template called!\n", \
+	__FILE__, __LINE__, __FUNCTION__); \
+	std::terminate();
+	template<typename T> int32_t activateMenu(T*)      { EMERGENCY_OUT }
+	template<typename T> void    activateText(T*, int) { EMERGENCY_OUT }
+	template<typename T> void    activateToggle(T*)    { EMERGENCY_OUT }
+	template<typename T> void    displayMenu(T*)       { EMERGENCY_OUT }
+	template<typename T> void    displayText(T*)       { EMERGENCY_OUT }
+	template<typename T> void    displayToggle(T*)     { EMERGENCY_OUT }
+#undef EMERGENCY_OUT
+
+
+	/* -------------------------
+	 * --- Protected members ---
+	 * -------------------------
+	 */
+
+	BUTTON*         button    = nullptr;    //!< The button used by ET_BUTTON.
+	int32_t         color     = BLACK;      //!< The color to use for the text, mainly useful for OT_TOGGLE.
+	bool            cursor_on = false;      //!< selected ET_TEXT elements feature a cursor.
+	int32_t         curs_clk  = 0;          //!< Only react on every CURSOR_FLIP_TIME call.
+	bool            decorated = false;      //!< Set to true by displayDeco() and false by clear_display(true)
+	bool            drawn     = false;      //!< Set to true by display methods, and false by clear_display().
+	int32_t         entryNum  = 0;          //!< Store the currently displayed text index with OT_VALUE options.
+	const char*     format    = nullptr;    //!< Format string to use with OT_VALUE
+	int32_t         height    = 0;          //!< Height of the display area.
+	int32_t         keyCode   = 0;          //!< Key Code returned when clicking an ET_BUTTON.
+	int32_t         left      = 0;          //!< Left x position of the display area.
+	OptionItemBase* next      = nullptr;    //!< Next option entry in a doubly linked list.
+	int32_t         padding   = 2;          //!< Padding of the title and possible buttons to the display area.
+	OptionItemBase* prev      = nullptr;    //!< Previous option entry in a doubly linked list.
+	bool            read_only = true;       //!< Whether ET_TEXT reacts on clicks and keys or not.
+	bool            selected  = false;      //!< Whether this entry is selected or not.
+	bool            show_menu = true;       //!< If set to true, the sub menu indicator is shown.
+	int32_t         show_size = 0;          //!< Size of the color box ET_COLOR displays the current color in
+	int32_t         textLen   = 0;          //!< Store the current size of OT_TEXT content.
+	eTextClass      textClass = TC_NONE;    //!< Noted for language switch.
+	bool            textOnly  = false;      //!< If set to true, displayText() draws no box.
+	const char**    texts     = nullptr;    //!< Text array to use for OT_VALUE
+	const char*     title     = nullptr;    //!< Title to display, mandatory
+	int32_t         titleIdx  = 0;          //!< Noted for language switch. -1 means the title is fixed.
+	int32_t         titleLen  = 0;          //!< Length of the title in pixels
+	int32_t         top       = 0;          //!< Top y position of the display area.
+	eEntryType      type      = ET_NONE;    //!< Type of the option, mandatory
+	int32_t         width     = 0;          //!< Width of the display area.
+};
+
+#endif // ATANKS_SRC_OPTIONITEMBASE_H_INCLUDED
+
diff --git a/src/optionitemcolour.cpp b/src/optionitemcolour.cpp
new file mode 100644
index 0000000..00d1255
--- /dev/null
+++ b/src/optionitemcolour.cpp
@@ -0,0 +1,203 @@
+#include "optionitemcolour.h"
+
+
+/** @brief Default constructor.
+  *
+  * The target is the color instance to handle.
+  *
+  * @param[in,out] player_ Pointer to the PLAYER instance to handle.
+  * @param[in,out] action_ Pointer to the action function handling the button click.
+  * @param[in] title_ The title of the option to display.
+  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+  * @param[in] top_ Top position of the display area.
+  * @param[in] left_ Left position of the display area.
+  * @param[in] width_ Width of the display area.
+  * @param[in] height_ Height of the display area.
+  * @param[in] padding_ Padding of the title and buttons to the display area.
+**/
+OptionItemColour::OptionItemColour(
+            int32_t*      color_,
+            const char*   title_,
+            int32_t       titleIdx_,
+            int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+            int32_t padding_, int32_t show_size_) :
+	OptionItemBase(ET_COLOR,
+	               title_,
+	               titleIdx_,
+	               nullptr,
+	               color_ ? *color_: WHITE, // WHITE to indicate an error.
+	               TC_NONE, nullptr,
+	               top_, left_, width_, height_, padding_, show_size_)
+{
+	// For ET_COLOR, only the color_ is needed
+	assert (color_ && "ERROR: color_ must be set");
+	tgt_color = color_;
+
+	// Create the rainbow box:
+	tgt_bitmap = create_bitmap(width, height);
+
+	assert((width  <= tgt_bitmap->w)  && "Width too small");
+	assert((height <= tgt_bitmap->h) && "Height too small");
+
+	solid_mode();
+	clear_to_color (tgt_bitmap, BLACK);
+	int32_t r, g, b;
+
+	for (int32_t x = 0.; x <= width; ++x) {
+		for (int32_t y = 0.; y <= height; ++y) {
+			double h = static_cast<double>(x) / static_cast<double>(width) * 360.;
+			double p = static_cast<double>(y) / static_cast<double>(height);
+			double v = 1.;
+			double s = p * 2.;
+
+			if (p > 0.5) {
+				v = 1. - ((p - 0.5) * 2.);
+				s = 1.;
+			}
+
+			assert ( (h >= 0.) && (h <= 360.) && "h out of range");
+			assert ( (s >= 0.) && (s <=   1.) && "s out of range");
+			assert ( (v >= 0.) && (v <=   1.) && "v out of range");
+
+			hsv_to_rgb(h, s, v, &r, &g, &b);
+
+			putpixel(tgt_bitmap, x + 1, y + 1, makecol(r, g, b));
+		}
+	}
+}
+
+
+/// @brief default dtor only setting nullptr values. No further action needed.
+OptionItemColour::~OptionItemColour()
+{
+	destroy_bitmap(tgt_bitmap);
+	tgt_bitmap = nullptr;
+	tgt_color  = nullptr;
+
+}
+
+
+/* ----------------------
+ * --- Public methods ---
+ * ----------------------
+ */
+
+/** @brief pick a new color from @a x / @a y and store it in @a target
+  *
+  * @param[in] x The x coordinate of the pixel to pick the colour from.
+  * @param[in] y The y coordinate of the pixel to pick the colour from.
+  */
+int32_t OptionItemColour::activate(int32_t, int32_t x, int32_t y, int32_t)
+{
+	int32_t pick_x = x - left;
+	int32_t pick_y = y - top;
+	if ( (pick_x >= 1)
+	  && (pick_y >= 1)
+	  && (pick_x <= (width  - 1))
+	  && (pick_y <= (height - 1)) ) {
+
+		// Note down where to draw the cross
+		act_x = pick_x;
+		act_y = pick_y;
+
+		// To show both, do a redraw
+		clear_display(false);
+		display(false);
+
+		// Get fresh pixel
+		*tgt_color = getpixel(tgt_bitmap, pick_x, pick_y);
+
+		// Draw helper cross:
+		displayCross();
+
+		// Redraw decoration:
+		this->displayDeco(*tgt_color);
+	} else {
+		// No cross
+		act_x = 0;
+		act_y = 0;
+	}
+
+	return 0;
+}
+
+
+/// @brief returns always true
+bool OptionItemColour::canGoDown()
+{
+	return true;
+}
+
+
+/// @brief returns always true
+bool OptionItemColour::canGoUp()
+{
+	return true;
+}
+
+
+/** @brief Display a color chooser for @a target and the display box.
+  *
+  * The display area is where to draw the "rainbow box". The box in which
+  * to draw the currently selected color is a "deco", but not drawn by
+  * display_deco(), because it would not have access to the target.
+  *
+  * @param[in] show_full decorations are drawn if this is set to true
+  */
+void OptionItemColour::display(bool show_full)
+{
+	if (!drawn) {
+
+		// Here is the chooser, the display box is "deco"
+		blit(tgt_bitmap, global.canvas, 0, 0, left, top, width, height);
+		rect (global.canvas, left, top, left + width, top + height, BLACK);
+
+		// Draw helper cross
+		displayCross();
+
+		drawn = true;
+	}
+
+	if (show_full)
+		this->displayDeco(*tgt_color);
+}
+
+
+/// @brief Draw the selector cross
+void OptionItemColour::displayCross()
+{
+	if ( (act_x > 0) && (act_y > 0) ) {
+		// To make the color picking easier, draw a cross
+		// to show where the user clicked in
+		int32_t right  = left + width;
+		int32_t bottom = top  + height;
+		int32_t x      = left + act_x;
+		int32_t y      = top  + act_y;
+		int32_t xl     = x - 5;
+		int32_t xr     = x + 5;
+		int32_t yt     = y - 5;
+		int32_t yb     = y + 5;
+
+		// Do not overdraw:
+		if (xl <= left  ) xl = x > left   ? left   + 1 : 0;
+		if (yt <= top   ) yt = y > top    ? top    + 1 : 0;
+		if (xr >= right ) xr = x < right  ? right  - 1 : 0;
+		if (yb >= bottom) yb = y < bottom ? bottom - 1 : 0;
+
+		// If a coordinate is zero, the line is not drawn.
+		int32_t cross_col = makecol(255 - getr(*tgt_color),
+		                            255 - getg(*tgt_color),
+		                            255 - getb(*tgt_color) );
+		if (xl) hline (global.canvas, xl,    y,     x - 1, cross_col);
+		if (xr) hline (global.canvas, x + 1, y,     xr,    cross_col);
+		if (yt) vline (global.canvas, x,     yt,    y - 1, cross_col);
+		if (yb) vline (global.canvas, x,     y + 1, yb,    cross_col);
+	}
+}
+
+
+/// @brief return true, the action function must be able to return an exit code.
+bool OptionItemColour::isExitButton()
+{
+	return true;
+}
diff --git a/src/optionitemcolour.h b/src/optionitemcolour.h
new file mode 100644
index 0000000..890a956
--- /dev/null
+++ b/src/optionitemcolour.h
@@ -0,0 +1,94 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONITEMCOLOUR_H_INCLUDED
+#define ATANKS_SRC_OPTIONITEMCOLOUR_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "optionitembase.h"
+
+/** @file optionitemcolour.h
+  * @brief declaration of the option entry class specialized on handling
+  * int32_t instances representing colours.
+**/
+
+
+/** @class OptionItemColour
+  * @brief abstract one option menu entry to handle an int32_t instance
+  *
+  * This class is a special version of the OptionItem template that can only
+  * handle int32_t instances representing colours.
+  *
+  * The the only entry type supported is the ET_COLOR.
+**/
+class OptionItemColour : public OptionItemBase
+{
+public:
+
+	/* -------------------------------------------
+	 * --- Public constructors and destructors ---
+	 * -------------------------------------------
+	 */
+
+	explicit
+	OptionItemColour(
+				int32_t*      color_,
+				const char*   title_,
+				int32_t       titleIdx_,
+				int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+				int32_t padding_, int32_t show_size_);
+	virtual ~OptionItemColour();
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	virtual int32_t activate    (int32_t, int32_t, int32_t, int32_t);
+	virtual bool    canGoDown   ();
+	virtual bool    canGoUp     ();
+	virtual void    display     (bool show_full);
+	virtual bool    isExitButton();
+	void            setLanguage ();
+
+private:
+
+	/* ----------------------------------------------
+	 * --- Private methods and external functions ---
+	 * ----------------------------------------------
+	 */
+
+
+	void displayCross();
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	int32_t  act_x      = 0;
+	int32_t  act_y      = 0;
+	BITMAP*  tgt_bitmap = nullptr; //!< Pre-drawn Rainbow box
+	int32_t* tgt_color  = nullptr; //!< Colour instance to handle
+};
+
+
+#endif // ATANKS_SRC_OPTIONITEMCOLOUR_H_INCLUDED
+
diff --git a/src/optionitemmenu.cpp b/src/optionitemmenu.cpp
new file mode 100644
index 0000000..efb5137
--- /dev/null
+++ b/src/optionitemmenu.cpp
@@ -0,0 +1,123 @@
+#include "optionitemmenu.h"
+#include "menu.h"
+#include "clock.h"
+
+/** @brief Default constructor.
+  *
+  * The target is the Menu instance to handle.
+  *
+  * Activation is a simple call to the Menus operator(), its return value
+  * is then returned without further ado.
+  *
+  * @param[in,out] menu_ Pointer to the Menu instance to handle.
+  * @param[in] title_ The title of the option to display.
+  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+  * @param[in] color Regular display color of the title.
+  * @param[in] top_ Top position of the display area.
+  * @param[in] left_ Left position of the display area.
+  * @param[in] width_ Width of the display area.
+  * @param[in] height_ Height of the display area.
+  * @param[in] padding_ Padding of the title and buttons to the display area.
+**/
+OptionItemMenu::OptionItemMenu(
+            Menu*         menu_,
+            const char*   title_,
+            int32_t       titleIdx_,
+            int32_t       color_,
+            int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+            int32_t padding_) :
+	OptionItemBase(ET_MENU,
+	               title_ ? title_ : menu_ ? menu_->getTitle() : nullptr,
+	               titleIdx_,
+	               nullptr, color_,
+	               TC_NONE, nullptr,
+	               top_, left_, width_, height_, padding_, 0),
+	menu(menu_)
+{
+	// Both action or player must be set
+	assert ( menu_ && "A nullptr menu_ makes no sense...");
+	// As the title is displayed as text, textOnly must be set:
+	this->textOnly = true;
+}
+
+
+/// @brief default dtor only setting nullptr values. No further action needed.
+OptionItemMenu::~OptionItemMenu()
+{
+	menu = nullptr;
+}
+
+
+/* ----------------------
+ * --- Public methods ---
+ * ----------------------
+ */
+
+/** @brief activate the sub menu
+  *
+  * This calls operator() on the menu.
+  *
+  * Note: The parameters are defined by OptionItemBase but unused
+  * here.
+  *
+  * @return The return code of the sub menu.
+**/
+int32_t OptionItemMenu::activate(int32_t, int32_t, int32_t, int32_t)
+{
+	// Remove parent menu timer
+	WIN_CLOCK_REMOVE
+
+	int32_t result = (*menu)();
+
+	// Changes are displayed at once:
+	this->drawn = false;
+	this->display(false);
+
+	// Re-add parent menu timer
+	WIN_CLOCK_INIT
+
+	return result;
+}
+
+
+/// @brief returns always true
+bool OptionItemMenu::canGoDown()
+{
+	return true;
+}
+
+
+/// @brief returns always true
+bool OptionItemMenu::canGoUp()
+{
+	return true;
+}
+
+
+/** @brief display the sub menu title
+  *
+  * @param[in] show_full If set to true, title and buttons are redrawn.
+**/
+void OptionItemMenu::display(bool show_full)
+{
+	this->displayMenu(menu);
+
+	// Show decorations if wanted:
+	if (show_full)
+		this->displayDeco();
+}
+
+
+/// @brief return true, the menu must be able to return an exit code.
+bool OptionItemMenu::isExitButton()
+{
+	return true;
+}
+
+
+/// @brief simply calls setLanguage(false) on the target menu
+void OptionItemMenu::setLanguage()
+{
+	if (menu)
+		menu->setLanguage(false);
+}
diff --git a/src/optionitemmenu.h b/src/optionitemmenu.h
new file mode 100644
index 0000000..3d6e2d5
--- /dev/null
+++ b/src/optionitemmenu.h
@@ -0,0 +1,86 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONITEMMENU_H_INCLUDED
+#define ATANKS_SRC_OPTIONITEMMENU_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "optionitembase.h"
+
+/** @file optionitemmenu.h
+  * @brief declaration of the option entry class specialized on handling
+  * Menu instances
+**/
+
+
+/** @class OptionItemMenu
+  * @brief abstract one option menu entry to handle a Menu instance
+  *
+  * This class is a special version of the OptionItem template that can only
+  * handle Menu instances.
+  *
+  * The the only entry type supported is the ET_MENU.
+**/
+class OptionItemMenu : public OptionItemBase
+{
+public:
+
+	/* -------------------------------------------
+	 * --- Public constructors and destructors ---
+	 * -------------------------------------------
+	 */
+
+	explicit
+	OptionItemMenu(Menu*         menu_,
+	               const char*   title_,
+	               int32_t       titleIdx_,
+	               int32_t color_,
+	               int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+	               int32_t padding_);
+	virtual ~OptionItemMenu();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	virtual int32_t activate    (int32_t, int32_t, int32_t, int32_t);
+	virtual bool    canGoDown   ();
+	virtual bool    canGoUp     ();
+	virtual void    display     (bool show_full);
+	virtual bool    isExitButton();
+	void            setLanguage ();
+
+private:
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	Menu* menu  = nullptr;  //!< Menu instance to handle
+};
+
+
+#endif // ATANKS_SRC_OPTIONITEMMENU_H_INCLUDED
+
+
+
diff --git a/src/optionitemplayer.cpp b/src/optionitemplayer.cpp
new file mode 100644
index 0000000..7bca9a2
--- /dev/null
+++ b/src/optionitemplayer.cpp
@@ -0,0 +1,231 @@
+#include "optionitemplayer.h"
+#include "player.h"
+#include "floattext.h"
+
+/** @brief Default constructor.
+  *
+  * The target is the player instance to handle. And the @a action_ function
+  * must do this if set.
+  *
+  * The target is a pointer pointer, so the class can be used for both
+  * editing and creating a player.
+  *
+  * If @a title_ is nullptr, the player name and color are used. The set
+  * title and BLACK otherwise.
+  *
+  * This class can be both, an ET_MENU (If an @a action_ function is set)
+  * or an ET_TOGGLE otherwise.
+  *
+  * @param[in,out] player_ Pointer to the PLAYER instance to handle.
+  * @param[in,out] action_ Pointer to the action function handling the button click.
+  * @param[in] title_ The title of the option to display.
+  * @param[in] titleIdx_ Index value of the submitted title. -1 means @a title_ is fixed.
+  * @param[in] top_ Top position of the display area.
+  * @param[in] left_ Left position of the display area.
+  * @param[in] width_ Width of the display area.
+  * @param[in] height_ Height of the display area.
+  * @param[in] padding_ Padding of the title and buttons to the display area.
+**/
+OptionItemPlayer::OptionItemPlayer(
+            PLAYER** player_,
+            int32_t (*action_)(PLAYER** player_, int32_t),
+            const char*  title_,
+            int32_t      titleIdx_,
+            int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+            int32_t padding_) :
+	OptionItemBase(ET_NONE,
+	               title_ ? title_
+	               : (player_ && *player_)
+	                 ? (*player_)->getName()
+	                 : nullptr,
+	               titleIdx_,
+	               nullptr,
+	               title_ ? SILVER
+	               : (player_ && *player_)
+	                 ? (*player_)->color
+	               : WHITE, // WHITE to indicate an error.
+	               TC_NONE, nullptr,
+	               top_, left_, width_, height_, padding_, 0)
+{
+	// For ET_TOGGLE, only the player_ is needed
+	assert (player_ && "ERROR: player_ must be set");
+
+	// For ET_MENU, action_ must be set, too, and for ET_TOGGLE *player_ must be set.
+	assert ( (action_ || (player_ && *player_ ) )
+		  && "ERROR: If no action_ function is set, *player_ must be valid");
+	actionFunc = action_;
+	player     = player_;
+
+	if (actionFunc) {
+		this->type = ET_MENU;
+
+		// If this is a regular player, no menu indicator is needed.
+		if (nullptr == title_)
+			this->show_menu = false;
+	} else if (player && *player)
+		this->type = ET_TOGGLE;
+}
+
+
+/// @brief default dtor only setting nullptr values. No further action needed.
+OptionItemPlayer::~OptionItemPlayer()
+{
+	actionFunc  = nullptr;
+	player      = nullptr;
+}
+
+
+/* ----------------------
+ * --- Public methods ---
+ * ----------------------
+ */
+
+/** @brief activate the player sub menu
+  *
+  * This calls the provided action function.
+  *
+  * Note: The parameters are defined by OptionItemBase but unused
+  * here.
+  *
+  * @return The return code of the action function.
+**/
+int32_t OptionItemPlayer::activate(int32_t, int32_t, int32_t, int32_t)
+{
+	int32_t result = -1;
+
+	if (ET_MENU == this->type)
+		result = actionFunc(player, 0);
+	else if (ET_TOGGLE == this->type)
+		this->activateToggle(&(*player)->selected);
+
+	// Changes are displayed at once:
+	if (ET_NONE != this->type)
+		this->clear_display(true);
+
+	return result;
+}
+
+
+/// @brief returns always true
+bool OptionItemPlayer::canGoDown()
+{
+	return true;
+}
+
+
+/// @brief returns always true
+bool OptionItemPlayer::canGoUp()
+{
+	return true;
+}
+
+
+/** @brief display the sub menu title
+  *
+  * @param[in] show_full If set to true, title and buttons are redrawn.
+**/
+void OptionItemPlayer::display(bool show_full)
+{
+	static const int32_t team_col_hi       = 0xc0;
+	static const int32_t team_col_mi       = 0x40;
+	static const int32_t team_col_lo       = 0x18;
+	static const char*   team_Indicator[4] = { "S", "N", "J", "?" };
+	static const int32_t team_color_bg[4]  = {
+		makecol(team_col_mi, team_col_lo, team_col_lo),
+		makecol(team_col_lo, team_col_mi, team_col_lo),
+		makecol(team_col_lo, team_col_lo, team_col_mi),
+		makecol(team_col_mi, team_col_mi, team_col_mi)
+	};
+	static const int32_t team_color_fg[4]  = {
+		makecol(team_col_hi, team_col_mi, team_col_mi),
+		makecol(team_col_mi, team_col_hi, team_col_mi),
+		makecol(team_col_mi, team_col_mi, team_col_hi),
+		makecol(team_col_hi, team_col_hi, team_col_hi)
+	};
+
+	if (!drawn) {
+		// Be sure to have the current name and color:
+		color = player && *player ? (*player)->color : color;
+		if ( player && *player && (!title || (strcmp((*player)->getName(), title))) )
+			setTitle((*player)->getName());
+
+		// Now display the player
+		int32_t tWidth   = -1 == titleIdx ? text_length(font, "W") + padding + 4 : 0;
+		int32_t xOff     = -1 == titleIdx ? 15 + padding + tWidth : 0;
+		int32_t txtLeft  = left + xOff;
+		int32_t txtColor = color;
+		int32_t xTop     = top + 1;
+		int32_t xHeight  = height - 2;
+
+		// If this is a toggle, it must be displayed first:
+		if (ET_TOGGLE == this->type) {
+			int32_t bgColor = BLACK;
+			int32_t shColor = makecol(getr(color) / 3, getg(color) / 3, getb(color) / 3);
+
+			// Swap colors if the player is selected
+			if ((*player)->selected) {
+				bgColor  = color;
+				txtColor = BLACK;
+			}
+
+			// Add a button like area for the name
+			rect(    global.canvas, txtLeft,     top,     left + width,     top + height,     txtColor);
+			rect(    global.canvas, txtLeft + 1, top + 1, left + width - 1, top + height - 1, txtColor);
+			hline(   global.canvas, txtLeft + 1,      top + height - 1, left + width - 1, shColor);
+			hline(   global.canvas, txtLeft,          top + height,     left + width,     shColor);
+			vline(   global.canvas, left + width - 1, top + 1,          top + height - 1, shColor);
+			vline(   global.canvas, left + width,     top,              top + height,     shColor);
+			rectfill(global.canvas, txtLeft + 2, top + 2, left + width - 2, top + height - 2, bgColor);
+
+			// Additional text offset for the border
+			txtLeft += 3;
+			xHeight -= 2;
+		}
+
+		// Then display the player name
+		eTeamTypes pTeam = player && *player ? (*player)->team : TEAM_COUNT;
+		if (title && title[0]) {
+
+			// Is the text shadowed, then create one:
+			if (env.shadowedText)
+				textout_ex (global.canvas, font, title, txtLeft + 2, xTop + 2,
+				            GetShadeColor(txtColor, true, PINK), -1);
+
+			textout_ex (global.canvas, font, title, txtLeft + 1, xTop + 1, txtColor, -1);
+		}
+
+		// Second the player type indicator:
+		if (-1 == titleIdx ) {
+			(*player)->drawIndicator(left, xTop, xHeight);
+
+			// and third the team indicator:
+			int32_t xLeft = left + 19;
+
+			rectfill(global.canvas, xLeft + 1, xTop + 3,
+			         xLeft + 12, xTop + xHeight - 2,
+			         team_color_bg[pTeam]);
+			rect(global.canvas, xLeft, xTop + 2, xLeft + 13, xTop + xHeight - 1,
+			     team_color_fg[pTeam]);
+
+			xLeft += 7;
+
+			textout_centre_ex(global.canvas, font, team_Indicator[pTeam],
+			                  xLeft - (TEAM_SITH == pTeam ? 1 : 0), xTop + 1,
+			                  team_color_fg[pTeam], -1);
+		} // end of player indicator
+
+		global.make_update (left, top, width, height);
+		drawn = true;
+	}
+
+	// Show decorations if wanted:
+	if (show_full)
+		this->displayDeco();
+}
+
+
+/// @brief return true, the action function must be able to return an exit code.
+bool OptionItemPlayer::isExitButton()
+{
+	return true;
+}
diff --git a/src/optionitemplayer.h b/src/optionitemplayer.h
new file mode 100644
index 0000000..e9c597b
--- /dev/null
+++ b/src/optionitemplayer.h
@@ -0,0 +1,92 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONITEMPLAYER_H_INCLUDED
+#define ATANKS_SRC_OPTIONITEMPLAYER_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include "optionitembase.h"
+
+/** @file optionitemplayer.h
+  * @brief declaration of the option entry class specialized on handling
+  * PLAYER instances
+**/
+
+
+/** @class OptionItemPlayer
+  * @brief abstract one option menu entry to handle a PLAYER instance
+  *
+  * This class is a special version of the OptionItem template that can only
+  * handle PLAYER instances.
+  *
+  * The the only entry type supported is the ET_MENU.
+**/
+class OptionItemPlayer : public OptionItemBase
+{
+public:
+
+	/* -------------------------------------------
+	 * --- Public constructors and destructors ---
+	 * -------------------------------------------
+	 */
+
+	explicit
+	OptionItemPlayer(
+	            PLAYER**      player_,
+	            int32_t     (*action_)(PLAYER** player_, int32_t),
+	            const char*   title_,
+	            int32_t       titleIdx_,
+	            int32_t top_, int32_t left_, int32_t width_, int32_t height_,
+	            int32_t padding_);
+	virtual ~OptionItemPlayer();
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	virtual int32_t activate    (int32_t, int32_t, int32_t, int32_t);
+	virtual bool    canGoDown   ();
+	virtual bool    canGoUp     ();
+	virtual void    display     (bool show_full);
+	virtual bool    isExitButton();
+	void            setLanguage ();
+
+private:
+
+	/* ----------------------------------------------
+	 * --- Private methods and external functions ---
+	 * ----------------------------------------------
+	 */
+
+	int32_t (*actionFunc )(PLAYER** target, int32_t) = nullptr;
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	PLAYER** player  = nullptr;  //!< PLAYER instance to handle
+};
+
+
+#endif // ATANKS_SRC_OPTIONITEMPLAYER_H_INCLUDED
+
+
diff --git a/src/optionscreens.cpp b/src/optionscreens.cpp
new file mode 100644
index 0000000..5f5b899
--- /dev/null
+++ b/src/optionscreens.cpp
@@ -0,0 +1,1038 @@
+#include "optionscreens.h"
+#include "player.h"
+#include "files.h"
+#include "sound.h"
+
+// Helper functions to build the sub menus for the options screen
+static void build_Physics (Menu &mPhysics,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY);
+static void build_Weather (Menu &mWeather,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY);
+static void build_Graphics(Menu &mGraphics,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY);
+static void build_Money   (Menu &mMoney,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY);
+static void build_Network (Menu &mNetwork,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY);
+static void build_Sound   (Menu &mSound,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY);
+
+// Helper action function to do direct language changes
+#define LANG_SWITCH_TRIGGER 0x0BadCafe
+int32_t switch_language(eLanguages* lang, int32_t val);
+
+
+/** @brief draw the Menu Background
+  *
+  * Draws a 600x400 centred box, fills it with some random lines or circles.
+  * Someday, we should make this more generic; have it take the box dimensions
+  * as an input parameter.
+**/
+void drawMenuBackground (eBackgroundTypes backType, int32_t tOffset, int32_t numItems)
+{
+	rectfill (global.canvas, env.halfWidth - 300, env.menuBeginY, // 100,
+	          env.halfWidth + 300, env.menuEndY, // env.screenHeight - 100,
+	          makecol (0, 79, 0));
+	rect     (global.canvas, env.halfWidth - 300, env.menuBeginY, // 100,
+	          env.halfWidth + 300, env.menuEndY, // env.screenHeight - 100,
+	          makecol (128, 255, 128));
+
+	if (BACKGROUND_BLANK == backType)
+		return;
+
+	drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
+	global.current_drawing_mode = DRAW_MODE_TRANS;
+	set_trans_blender (0, 0, 0, 15);
+
+	for (int32_t tCount = 0; tCount < numItems; tCount++) {
+		int32_t radius = static_cast<int32_t>( (perlin1DPoint (1.0, 5,
+		                                   (tOffset * 0.0333) + tCount + 423346,
+		                                     0.5, 8) + 1.1) * 20); // [0.1;2.1]
+		int32_t xpos = env.halfWidth
+		             + static_cast<int32_t>(perlin1DPoint (1.0, 3,
+		                                   (tOffset * 0.0166) + tCount + 232662,
+		                                     0.3, 3) * (299 - radius));
+		int32_t ypos = env.halfHeight
+		             + static_cast<int32_t>(perlin1DPoint (1.0, 2,
+		                                   (tOffset * 0.0175) + tCount + 42397,
+		                                     0.3, 3)
+		                                   * (env.halfHeight - env.menuBeginY
+		                                      - radius - 1));
+		switch (backType) {
+			case BACKGROUND_CIRCLE:
+				circlefill (global.canvas, xpos, ypos, radius, LIME_GREEN);
+				break;
+			case BACKGROUND_LINE:
+				rectfill (global.canvas, xpos - radius / 2, env.menuBeginY + 1,
+				          xpos + radius / 2, env.menuEndY - 1, LIME_GREEN);
+				break;
+			case BACKGROUND_SQUARE:
+				rectfill (global.canvas, xpos - radius, ypos - radius,
+				          xpos + radius, ypos + radius, LIME_GREEN);
+				break;
+			case BACKGROUND_BLANK:
+			default:
+				break;
+		}
+
+	}
+
+	solid_mode();
+	global.current_drawing_mode = DRAW_MODE_SOLID;
+}
+
+
+/// @brief Show a screen listing all players allowing to create new and edit existing ones.
+void editPlayers ()
+{
+	/// @todo : Currently the width is fixed on 600. This should be made
+	/// more dynamic like the height. Although the height is fixed, too...
+	/// However, there is much to do to get an adaptable and good looking menu...
+
+	int32_t optionsRetVal = 0;
+	int32_t menuMid       = 300;
+	int32_t itemHeight    = env.fontHeight + 2;
+	int32_t itemPadding   = 2;
+	int32_t itemY         = (itemHeight + itemPadding) * 2;
+	int32_t btnHeight     = env.misc[7]->h + itemPadding;
+	int32_t menuHeight    = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t plListHeight  = menuHeight - itemY             // Top area reserved for the title
+	                      - btnHeight - itemPadding - 2;   // Bottom area reserved for buttons
+	// "Select Players"
+	Menu menu(MC_PLAYERS, env.halfWidth - menuMid, env.menuBeginY);
+
+	// "Create New"
+	PLAYER* player_new = nullptr;
+	int32_t first_idx = menu.addMenu(&player_new, new_player, 1,
+	                                 menuMid - 53, itemY,
+	                                 100, itemHeight, itemPadding);
+	itemY += itemHeight + itemPadding;
+
+	// Add one entry per player
+	// One Menu per player:
+	// Add one edit option per player
+	int32_t last_idx  = first_idx;
+	int32_t max_width = 100;
+	plListHeight -= itemY; // this is left.
+
+	// First loop to determine maximum name width
+	for (int32_t num = 0; num < env.numPermanentPlayers; num++) {
+		int32_t xLen = text_length(font, env.allPlayers[num]->getName());
+		if (xLen > max_width)
+			max_width = xLen;
+	}
+
+	// Now really add them
+	for (int32_t num = 0; num < env.numPermanentPlayers; num++)
+		last_idx = menu.addMenu(&env.allPlayers[num], edit_player, -1,
+		                        0, 0, max_width + 15, itemHeight, itemPadding);
+	// last_idx is one too high now
+	last_idx--;
+
+	// Distribute the player list:
+	menu.distribute(first_idx, last_idx, menuMid * 2, plListHeight, itemY, false);
+
+	// Add "back" button
+	menu.addButton( 2, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - (env.misc[7]->w / 2),
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+
+	while ( (KEY_ESC != optionsRetVal)
+	     && !global.isCloseBtnPressed() ) {
+		optionsRetVal = menu();
+
+		// Was an edit confirmed?
+		if (PE_CONFIRM_EDIT & optionsRetVal)
+			menu.redraw(optionsRetVal ^ PE_CONFIRM_EDIT, true);
+		else if (PE_CONFIRM_DEL & optionsRetVal) {
+			// delete the menu entry
+			int32_t num = optionsRetVal ^ PE_CONFIRM_DEL;
+			menu.delete_entry(num + first_idx);
+			--last_idx;
+
+			// The player has to be deleted, too:
+			PLAYER* to_delete = env.allPlayers[num];
+			env.deletePermPlayer(to_delete);
+
+			// redistribute the remaining:
+			menu.distribute(first_idx, last_idx, menuMid * 2, plListHeight, itemY, true);
+
+			optionsRetVal = 0;
+		} else if (PE_CONFIRM_NEW & optionsRetVal) {
+			if (player_new) {
+				// Add a menu entry for the new player:
+				menu.addMenu(&env.allPlayers[env.numPermanentPlayers - 1],
+				             edit_player, -1, 0, 0, max_width + 15, itemHeight,
+				             itemPadding);
+				// Move the new menu entry up, the "back" button is in the way
+				menu.move_entry(last_idx + 2, last_idx + 1);
+
+				// And re-distribute
+				menu.distribute(first_idx, ++last_idx, menuMid * 2,
+				                plListHeight, itemY, true);
+			}
+			optionsRetVal = 0;
+		}
+	}
+}
+
+
+/// @brief The main options menu
+void optionsMenu ()
+{
+	/// @todo : Currently the width is fixed on 600. This should be made
+	/// more dynamic like the height. Although the height is fixed, too...
+	/// However, there is much to do to get an adaptable and good looking menu...
+
+	int32_t optionsRetCode = 0;
+	int32_t menuMid        = 300;
+	int32_t itemWidth      = 210;
+	int32_t itemHeight     = env.fontHeight + 2;
+	int32_t itemPadding    = 2;
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t itemY          = itemFullHeight * 3;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t menuLeft       = env.halfWidth - menuMid;
+	int32_t menuTop        = env.menuBeginY;
+	int32_t idx            = 1;
+
+	Menu mMain    (MC_MAIN,     menuLeft, menuTop);
+	Menu mPhysics (MC_PHYSICS,  menuLeft, menuTop);
+	Menu mWeather (MC_WEATHER,  menuLeft, menuTop);
+	Menu mGraphics(MC_GRAPHICS, menuLeft, menuTop);
+	Menu mMoney   (MC_FINANCE,  menuLeft, menuTop);
+	Menu mNetwork (MC_NETWORK,  menuLeft, menuTop);
+	Menu mSound   (MC_SOUND,    menuLeft, menuTop);
+
+	// As the sub menus must be attached to the main options menu,
+	// they have to be build first, before the main menu can be built.
+	build_Sound   (mSound,    menuMid, itemWidth, itemHeight, itemPadding, itemY);
+	build_Network (mNetwork,  menuMid, itemWidth, itemHeight, itemPadding, itemY);
+	build_Money   (mMoney,    menuMid, itemWidth, itemHeight, itemPadding, itemY);
+	build_Graphics(mGraphics, menuMid, itemWidth, itemHeight, itemPadding, itemY);
+	build_Weather (mWeather,  menuMid, itemWidth, itemHeight, itemPadding, itemY);
+	build_Physics (mPhysics,  menuMid, itemWidth, itemHeight, itemPadding, itemY);
+
+	// Now the main options screen can be built:
+
+	// "Reset All"
+	Menu mReset(MC_RESET, menuLeft, menuTop);
+	mReset.addButton( 1, nullptr, RO_RESET,
+	                  env.misc[7], nullptr, env.misc[8], false,
+	                  menuMid + 50,
+	                  menuHeight- btnHeight - 6, 0, 0, itemPadding);
+	mReset.addButton( 2, nullptr, RO_BACK,
+	                  env.misc[7], nullptr, env.misc[8], false,
+	                  menuMid - env.misc[7]->w - 50,
+	                  menuHeight- btnHeight - 6, 0, 0, itemPadding);
+
+	// "Reset options"
+	mMain.addMenu   ( &mReset, idx++, RED, menuMid - (env.misc[7]->w / 2), itemY,
+	                  150, itemFullHeight, itemPadding);
+	itemY += btnHeight + itemPadding;
+
+	// "Physics"
+	mMain.addMenu(&mPhysics, idx++, WHITE, menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Weather"
+	mMain.addMenu(&mWeather, idx++, WHITE, menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Graphics"
+	mMain.addMenu(&mGraphics, idx++, WHITE, menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Money"
+	mMain.addMenu(&mMoney, idx++, WHITE, menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Network"
+	mMain.addMenu(&mNetwork, idx++, WHITE, menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Sound"
+	mMain.addMenu(&mSound, idx++, WHITE, menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Weapon Tech Level"
+	mMain.addValue(&env.weapontechLevel, idx++, WHITE, 0, 5, 1, "%d",
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Item Tech Level"
+	mMain.addValue(&env.itemtechLevel, idx++, WHITE, 0, 5, 1, "%d",
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Landscape"
+	mMain.addValue(&env.landType, idx++, nullptr, WHITE,
+	               TC_LANDTYPE, static_cast<int32_t>(LAND_NONE),
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Turn Order"
+	mMain.addValue(&env.turntype, idx++, nullptr, WHITE,
+	               TC_TURNTYPE, static_cast<int32_t>(TURN_SIMUL),
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Skip AI-only play"
+	mMain.addValue(&env.skipComputerPlay, idx++, nullptr, WHITE,
+	               TC_SKIPTYPE, static_cast<int32_t>(SKIP_HUMANS_DEAD),
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Show FPS"
+	mMain.addValue(&env.showFPS, idx++, nullptr, WHITE,
+	               TC_OFFON, 1,
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Language"
+	mMain.addValue(&env.language, switch_language, idx++, nullptr, WHITE,
+	               TC_LANGUAGE, static_cast<int32_t>(EL_ITALIAN),
+	               menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+	// "Back"
+	mMain.addButton(idx, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - (env.misc[7]->w / 2),
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+
+	// Safe the current language, if a new one is selected,
+	// the text files must be reloaded.
+	eLanguages cur_lang = env.language;
+
+	while (0 == optionsRetCode) {
+		int32_t old_fps = env.frames_per_second;
+
+		optionsRetCode = mMain();
+
+		if (RO_RESET == optionsRetCode) {
+			env.Reset_Options();
+			optionsRetCode = 0;
+		} else if (RO_BACK == optionsRetCode)
+			optionsRetCode = 0;
+		else if (LANG_SWITCH_TRIGGER == optionsRetCode) {
+			mMain.setLanguage(true);
+			optionsRetCode = 0;
+		}
+
+		// If no exit code is set, redraw the menu:
+		if (0 == optionsRetCode)
+			mMain.redrawAll(true);
+
+		// Update pre-calculated values if FPS has been changed:
+		if (old_fps != env.frames_per_second)
+			env.set_fps(0); // 0 triggers re-calculation only
+	} // End of menu loop
+
+	// Did the language change?
+	if (env.language != cur_lang) {
+		env.load_text_files();
+		Load_Weapons_Text();
+	}
+}
+
+
+/// @brief Show a screen that shows the preparations to create a new game.
+int32_t selectPlayers ()
+{
+	/// @todo : Currently the width is fixed on 600. This should be made
+	/// more dynamic like the height. Although the height is fixed, too...
+	/// However, there is much to do to get an adaptable and good looking menu...
+
+	int32_t optionsRetVal  = 0;
+	int32_t menuMid        = 300;
+	int32_t itemWidth      = 120;
+	int32_t itemHeight     = env.fontHeight + 4;
+	int32_t itemPadding    = 2;
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t itemY          = itemFullHeight * 2;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t plListHeight   = menuHeight - itemY           // Top area reserved for the title
+	                       - btnHeight - itemPadding - 2; // Bottom area reserved for buttons
+	int32_t idx            = 1;
+
+	uint32_t number_saved_games = 0;
+	dirent** saved_game_names;
+	char**   game_list = NULL;
+
+	// Use new menu system:
+	// "Select Players"
+	Menu menu(MC_PLAY, env.halfWidth - menuMid, env.menuBeginY);
+
+	// "Rounds"
+	menu.addValue(&env.rounds, idx++, BLACK, 1, MAX_ROUNDS, 1, "%u",
+	              menuMid - (itemWidth / 2), itemY, itemWidth, itemHeight,
+	              itemPadding);
+	itemY += itemFullHeight + 2;
+
+	// "New Game Name"
+	strncpy(env.game_name, "New Game", GAMENAMELEN);
+	menu.addText(env.game_name, idx++, GAMENAMELEN, BLACK, "%s",
+	             menuMid - (itemWidth / 2), itemY, itemWidth, itemHeight,
+	             itemPadding);
+	itemY += itemFullHeight + 2;
+
+	// find saved games
+	saved_game_names = Find_Saved_Games(number_saved_games);
+
+	if ( ( saved_game_names ) && ( number_saved_games ) ) {
+
+		// Extend the global list if it is too small
+		if (env.saved_game_list_size <= number_saved_games) {
+			// Note: The number of saved games in ENVRIONMENT must be
+			// one entry larger, as the text array for the option menu
+			// has to be terminated by a null pointer.
+			game_list = (char**)realloc(env.saved_game_list,
+			                            sizeof(char*) * (number_saved_games + 1));
+			if (game_list) {
+				// The old names must be freed and the new initialized
+				for (uint32_t i = 0; i <= number_saved_games ; ++i) {
+					if ( ( i < env.saved_game_list_size ) && game_list[i] )
+						free(game_list[i]);
+					game_list[i] = nullptr;
+				}
+
+				env.saved_game_list = const_cast<const char**>(game_list);
+				env.saved_game_list_size = number_saved_games + 1;
+			} else {
+				cerr << "ERROR extending saved game list from ";
+				cerr << env.saved_game_list_size << " entries to ";
+				cerr << number_saved_games << " entries." << endl;
+				free (saved_game_names);
+				return KEY_ESC;
+			}
+		} else
+			game_list = const_cast<char**>(env.saved_game_list);
+		// End of preparing space for the saved game list
+
+		// Copy the found names (Without the extra entry of course)
+		for (uint32_t i = 0; i < number_saved_games; ++i) {
+
+			// If the name is already set, free it, it was strdup'd
+			if ( game_list[i] )
+				free(game_list[i]);
+			game_list[i] = strdup(saved_game_names[i]->d_name);
+
+			// clear trailing extension
+			if ( strchr(game_list[i], '.') )
+				strchr(game_list[i], '.')[0] = '\0';
+		}
+
+		// set up menu for selecting saved games
+		// "or Load Game"
+		env.saved_gameindex = 0;
+		menu.addValue(&env.saved_gameindex, idx++, env.saved_game_list,
+		              BLACK, TC_FREETEXT, number_saved_games - 1,
+		              menuMid - (itemWidth / 2), itemY, itemWidth, itemHeight,
+					  itemPadding);
+		itemY += itemFullHeight + 4; // Next two options need more height
+
+		// "Load Game"
+		menu.addToggle(&env.loadGame, idx++, WHITE,
+					menuMid - 125, itemY, 100, itemHeight + 5, itemPadding);
+		// itemY stays, "Campaign" is on the same row.
+	} // End of having saved games
+
+	// If no saved games are there, idx must be advanced nevertheless or
+	// the texts go mixed up:
+	else
+		idx += 2;
+
+
+	// "Campaign"
+	// Note: And save the result, it is the first player index
+	int32_t first_idx = menu.addToggle(&env.campaign_mode, idx++, WHITE,
+	                                   menuMid + 25, itemY, 100, itemHeight + 5,
+	                                   itemPadding);
+	itemY += itemFullHeight + 7; // ET_TOGGLE needs more height
+
+	// Add one entry per player
+	int32_t last_idx  = first_idx;
+	int32_t max_width = 100;
+	plListHeight -= itemY; // this is left.
+
+	// First loop to determine maximum name width
+	for (int32_t num = 0; num < env.numPermanentPlayers; num++) {
+		int32_t xLen = text_length(font, env.allPlayers[num]->getName())
+		             + (2 * itemPadding);
+		if (xLen > max_width)
+			max_width = xLen;
+	}
+
+	// Now really add them
+	for (int32_t num = 0; num < env.numPermanentPlayers; num++)
+		last_idx = menu.addToggle(&env.allPlayers[num],
+		                          0, 0, max_width + 21, itemHeight, itemPadding);
+
+	// last_idx is one too high now.
+	last_idx--;
+
+
+	// Distribute the player list:
+	menu.distribute(first_idx, last_idx, menuMid * 2, plListHeight, itemY, false);
+
+	// The "Okay" and "Back" buttons have their own texts to be translated
+	menu.addButton( idx + 1, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - env.misc[7]->w - 25,
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+	menu.addButton( idx, nullptr, KEY_ENTER,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid + 25,
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+
+	// Let the menu take over user input until the game is either started,
+	// or the user opts out to the main menu.
+	// Idea for the future:
+	// Start the menu as a thread and do some fancy stuff with the background
+	// while we are waiting.
+	do {
+		optionsRetVal = menu();
+
+		if ( env.loadGame ) {
+			if ( env.saved_game_list
+			  && ( env.saved_gameindex < env.saved_game_list_size )
+			  && env.saved_game_list[env.saved_gameindex][0]) {
+
+				strncpy(env.game_name,
+				        env.saved_game_list[env.saved_gameindex],
+				        GAMENAMELEN);
+			}
+		}
+
+		if (optionsRetVal == KEY_ENTER) {
+			if (env.loadGame) {
+				// A set game shall be loaded
+				if (Check_For_Saved_Game())
+					optionsRetVal = MRC_Load_Game;
+				else {
+					optionsRetVal = 0;
+					errorMessage = env.ingame->Get_Line(39);
+					errorX  = env.halfWidth - text_length(font, errorMessage) / 2;
+					errorY = env.menuBeginY + itemFullHeight;
+				}
+			} else {
+				// Start a new game
+				int32_t playerCount = 0;
+				env.numGamePlayers  = 0;
+
+				// Add selected players to the game:
+				for (int z = 0; z < env.numPermanentPlayers; z++) {
+					if (env.allPlayers[z]->selected) {
+						env.addGamePlayer(env.allPlayers[z]);
+						playerCount++;
+					}
+				}
+
+				// Check selected players
+				if ((playerCount < 2) || (playerCount > MAXPLAYERS)) {
+					if (playerCount < 2)
+						errorMessage = env.ingame->Get_Line(8);
+					else if (playerCount > MAXPLAYERS)
+						errorMessage = env.ingame->Get_Line(9);
+
+					errorX = env.halfWidth - text_length(font, errorMessage) / 2;
+					errorY = env.menuBeginY + itemFullHeight;
+					optionsRetVal = 0;
+				} else
+					optionsRetVal = MRC_Play_Game;
+			} // End of loading versus starting anew
+		} // End of KEY_ENTER result
+
+		// zero means an error occured.
+		// keep running the loop until ESC is pressed or a non-zero value appears
+	} while ((KEY_ESC       != optionsRetVal)
+	      && (MRC_Play_Game != optionsRetVal)
+	      && (MRC_Load_Game != optionsRetVal)
+	      && !global.isCloseBtnPressed() );
+
+	if ( (KEY_ESC == optionsRetVal)
+	  || global.isCloseBtnPressed() )
+		optionsRetVal = MRC_Esc_Menu;
+
+	if (saved_game_names) {
+		for (uint32_t i = 0; i < number_saved_games; ++i) {
+			if ( saved_game_names[i] ) {
+
+#if defined(ATANKS_IS_WINDOWS)
+				// Under windows d_name is strdup'd
+				if ( saved_game_names[i]->d_name )
+					free(saved_game_names[i]->d_name);
+#endif // Windows
+
+				free(saved_game_names[i]);
+			}
+		}
+		free(saved_game_names);
+	}
+
+	return optionsRetVal;
+}
+
+
+// Helper functions to build the sub menus for the options screen
+static void build_Physics(Menu &mPhysics,
+                          int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                          int32_t itemPadding, int32_t itemY)
+{
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t idx            = 1;
+
+	assert( (0 == mPhysics.count()) && "ERROR: mPhysics already built?");
+
+	if (mPhysics.count())
+		return; // Don't build twice!
+
+	// "Gravity"
+	mPhysics.addValue(&env.gravity, idx++, WHITE, .025, .325, .025, "%5.3f",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Viscosity",
+	mPhysics.addValue(&env.viscosity, idx++, WHITE, .25, 2., .25, "%3.2f",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Land Slide"
+	mPhysics.addValue(&env.landSlideType, idx++, nullptr, WHITE,
+	                  TC_LANDSLIDE, static_cast<int32_t>(SLIDE_CARTOON),
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Land Slide Delay",
+	mPhysics.addValue(&env.landSlideDelay, idx++, WHITE, 1, 5, 1, "%d",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Wall Type"
+	mPhysics.addValue(&env.wallType, idx++, nullptr, WHITE,
+	                  TC_WALLTYPE, static_cast<int32_t>(WALL_RANDOM),
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Boxed Mode",
+	mPhysics.addValue(&env.boxedMode, idx++, nullptr, WHITE,
+	                  TC_OFFONRANDOM, static_cast<int32_t>(BM_RANDOM),
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Boxed Ceiling Wrapping",
+	mPhysics.addValue(&env.do_box_wrap, idx++, nullptr, WHITE,
+	                  TC_OFFON, static_cast<int32_t>(BM_ON),
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Violent Death"
+	mPhysics.addValue(&env.violent_death, idx++, nullptr, WHITE,
+	                  TC_LIGHTNING, static_cast<int32_t>(VD_HEAVY),
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Timed Shots"
+	mPhysics.addValue(&env.maxFireTime, idx++, WHITE, 0, 180, 5, "%d",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Volley Delay"
+	mPhysics.addValue(&env.volley_delay, idx++, WHITE, 5, 50, 1, "%d",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Explosion Debris"
+	mPhysics.addValue(&env.debris_level, idx++, nullptr, WHITE,
+	                  TC_LIGHTNING, 3,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+	// "Back" Button
+	mPhysics.addButton(idx, nullptr, KEY_ESC,
+	                   env.misc[7], nullptr, env.misc[8], false,
+	                   menuMid - (env.misc[7]->w / 2),
+	                   menuHeight - btnHeight - itemPadding - 2,
+	                   0, 0, 2);
+}
+
+
+static void build_Weather(Menu &mWeather,
+                          int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                          int32_t itemPadding, int32_t itemY)
+{
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t idx            = 1;
+
+	assert( (0 == mWeather.count()) && "ERROR: mWeather already built?");
+
+	if (mWeather.count())
+		return; // Don't build twice!
+
+
+	// "Meteor Showers"
+	mWeather.addValue(&env.meteors, idx++, nullptr, WHITE,
+	                  TC_METEOR, 3,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Lightning"
+	mWeather.addValue(&env.lightning, idx++, nullptr, WHITE,
+	                  TC_LIGHTNING, 3,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Falling Dirt"
+	mWeather.addValue(&env.falling_dirt_balls, idx++, nullptr, WHITE,
+	                  TC_METEOR, 3,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Laser Satellite"
+	mWeather.addValue(&env.satellite, idx++, nullptr, WHITE,
+	                  TC_SATELLITE, static_cast<int32_t>(SL_SUPER),
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Fog"
+	mWeather.addValue(&env.fog, idx++, nullptr, WHITE,
+	                  TC_OFFON, 1,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Max Wind Strength"
+	mWeather.addValue(&env.windstrength, idx++, WHITE, 0, 100, 5, "%d",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+
+	// "Wind Variation"
+	mWeather.addValue(&env.windvariation, idx++, WHITE, 0, 100, 3, "%d",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+	// "Back"
+	mWeather.addButton(idx, nullptr, KEY_ESC,
+	                   env.misc[7], nullptr, env.misc[8], false,
+	                   menuMid - (env.misc[7]->w / 2),
+	                   menuHeight - btnHeight - itemPadding - 2,
+	                   0, 0, 2);
+}
+
+
+static void build_Graphics(Menu &mGraphics,
+                           int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                           int32_t itemPadding, int32_t itemY)
+{
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t idx            = 1;
+
+	assert( (0 == mGraphics.count()) && "ERROR: mGraphics already built?");
+
+	if (mGraphics.count())
+		return; // Don't build twice!
+
+	// "Full Screen"
+	mGraphics.addValue(&env.full_screen, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Dithering"
+	mGraphics.addValue(&env.ditherGradients, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Detailed Land"
+	mGraphics.addValue(&env.detailedLandscape, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Detailed Sky"
+	mGraphics.addValue(&env.detailedSky, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Fading Text"
+	mGraphics.addValue(&env.fadingText, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Shadowed Text"
+	mGraphics.addValue(&env.shadowedText, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Swaying Text"
+	mGraphics.addValue(&env.swayingText, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Colour Theme"
+	mGraphics.addValue(&env.colourTheme, idx++, nullptr, WHITE,
+	                   TC_COLOUR, static_cast<int32_t>(CT_CRISPY),
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Screen Width"
+	mGraphics.addValue(&env.temp_screenWidth, idx++, WHITE, 800, 2000, 100, "%d",
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Screen Height"
+	mGraphics.addValue(&env.temp_screenHeight, idx++, WHITE, 600, 1400, 100, "%d",
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Mouse Pointer"
+	mGraphics.addValue(&env.osMouse, idx++, nullptr, WHITE,
+	                   TC_MOUSE, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Game Speed"
+	mGraphics.addValue(&env.frames_per_second, idx++, WHITE, 30, 1000, 5, "%d",
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Custom Background"
+	mGraphics.addValue(&env.custom_background, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Show AI Feedback"
+	mGraphics.addValue(&env.showAIFeedback, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Dynamic Menu Background"
+	mGraphics.addValue(&env.dynamicMenuBg, idx++, nullptr, WHITE,
+	                   TC_OFFON, 1,
+	                   menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+
+	// "Back"
+	mGraphics.addButton(idx, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - (env.misc[7]->w / 2),
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+}
+
+
+static void build_Money(Menu &mMoney,
+                        int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                        int32_t itemPadding, int32_t itemY)
+{
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t idx            = 1;
+
+	assert( (0 == mMoney.count()) && "ERROR: mMoney already built?");
+
+	if (mMoney.count())
+		return; // Don't build twice!
+
+	// "Starting Money"
+	mMoney.addValue(&env.startmoney, idx++, WHITE, 0, 200000, 5000, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Interest Rate"
+	mMoney.addValue(&env.interest, idx++, WHITE, 1., 1.5, .05, "%3.2f",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Round Win Bonus"
+	mMoney.addValue(&env.scoreRoundWinBonus, idx++, WHITE, 0, 50000, 5000, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Damage Bounty"
+	mMoney.addValue(&env.scoreHitUnit, idx++, WHITE, 0, 500, 25, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Self-Damage Penalty"
+	mMoney.addValue(&env.scoreSelfHit, idx++, WHITE, 0, 10000, 1000, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Team-Damage Penalty"
+	mMoney.addValue(&env.scoreTeamHit, idx++, WHITE, 0, 10000, 1000, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Tank Destruction Bonus"
+	mMoney.addValue(&env.scoreUnitDestroyBonus, idx++, WHITE, 0, 20000, 2500, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Tank Self-Destruction Penalty"
+	mMoney.addValue(&env.scoreUnitSelfDestroy, idx++, WHITE, 0, 20000, 2500, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Item Sell Multiplier"
+	mMoney.addValue(&env.sellpercent, idx++, WHITE, 0., 1., .1, "%2.2f",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Teams Share"
+	mMoney.addValue(&env.divide_money, idx++, nullptr, WHITE,
+	                TC_OFFON, 1,
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+	// "Back"
+	mMoney.addButton(idx, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - (env.misc[7]->w / 2),
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+}
+
+
+static void build_Network(Menu &mNetwork,
+                          int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                          int32_t itemPadding, int32_t itemY)
+{
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t idx            = 1;
+
+	assert( (0 == mNetwork.count()) && "ERROR: mNetwork already built?");
+
+	if (mNetwork.count())
+		return; // Don't build twice!
+
+
+	// "Check Updates"
+	mNetwork.addValue(&env.check_for_updates, idx++, nullptr, WHITE,
+	                  TC_OFFON, 1,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Networking"
+	mNetwork.addValue(&env.network_enabled, idx++, nullptr, WHITE,
+	                  TC_OFFON, 1,
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Listen Port"
+	mNetwork.addValue(&env.network_port, idx++, WHITE, 10645, 64645, 1000, "%d",
+	                  menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Server Address"
+	mNetwork.addText(env.server_name, idx++, 127, WHITE, "%s",
+	                 menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Server Port"
+	mNetwork.addText(env.server_port, idx++, 127, WHITE, "%s",
+	                 menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+	// "Back"
+	mNetwork.addButton(idx, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - (env.misc[7]->w / 2),
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+}
+
+
+static void build_Sound(Menu &mSound,
+                        int32_t menuMid, int32_t itemWidth, int32_t itemHeight,
+                        int32_t itemPadding, int32_t itemY)
+{
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+	int32_t idx            = 1;
+
+	assert( (0 == mSound.count()) && "ERROR: mSound already built?");
+
+	if (mSound.count())
+		return; // Don't build twice!
+
+	// "All Sound"
+	mSound.addValue(&env.sound_enabled, idx++, nullptr, WHITE,
+	                TC_OFFON, 1,
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Sound Driver"
+	mSound.addValue(&env.sound_driver, idx++, nullptr, WHITE,
+	                TC_SOUNDDRIVER, static_cast<int32_t>(SD_JACK),
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Music"
+	mSound.addValue(&env.play_music, idx++, nullptr, WHITE,
+	                TC_OFFON, 1,
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Volume Faktor"
+	mSound.addValue(&env.volume_factor, idx++, WHITE, 0, MAX_VOLUME_FACTOR, 1, "%d",
+	                menuMid - 50, itemY, itemWidth, itemHeight, itemPadding);
+
+	// "Back"
+	mSound.addButton(idx, nullptr, KEY_ESC,
+	                env.misc[7], nullptr, env.misc[8], false,
+	                menuMid - (env.misc[7]->w / 2),
+	                menuHeight - btnHeight - itemPadding - 2,
+	                0, 0, 2);
+}
+
+
+/// @brief Switch language helper function
+/// Note: The real use of this function is, that it generates a return
+///       code, so optionsMenu() can react on the language change. ;-)
+int32_t switch_language(eLanguages* lang, int32_t val)
+{
+    eLanguages old_lang = *lang;
+
+    if (val > 0)
+		++(*lang);
+	else if (val < 0)
+		--(*lang);
+
+	if (*lang != old_lang)
+		return LANG_SWITCH_TRIGGER;
+	return 0;
+}
+
diff --git a/src/imagedefs.h b/src/optionscreens.h
similarity index 64%
copy from src/imagedefs.h
copy to src/optionscreens.h
index f09fa5c..83cd06e 100644
--- a/src/imagedefs.h
+++ b/src/optionscreens.h
@@ -1,5 +1,6 @@
-#ifndef IMAGEDEFS_DEFINE
-#define IMAGEDEFS_DEFINE
+#pragma once
+#ifndef ATANKS_SRC_OPTIONSCREENS_H_INCLUDED
+#define ATANKS_SRC_OPTIONSCREENS_H_INCLUDED
 
 /*
  * atanks - obliterate each other with oversize weapons
@@ -12,18 +13,25 @@
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ * 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 
+ * 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.
  * */
 
-#define	DONE_IMAGE		11
-#define	FAST_UP_ARROW_IMAGE	12
-#define	UP_ARROW_IMAGE		13
-#define	DOWN_ARROW_IMAGE	14
-#define	FAST_DOWN_ARROW_IMAGE	15
+/** @file optionscreens.h
+  * @brief Here the functions providing option screens and menus are declared
+**/
+
+#include "menu.h"
+
+void    drawMenuBackground (int32_t itemType, int32_t tOffset, int32_t numItems);
+void    editPlayers        ();
+void    optionsMenu        ();
+int32_t selectPlayers      ();
+
+
+#endif // ATANKS_SRC_OPTIONSCREENS_H_INCLUDED
 
-#endif // IMAGEDEFS_DEFINE
diff --git a/src/optiontypes.cpp b/src/optiontypes.cpp
new file mode 100644
index 0000000..1541d6e
--- /dev/null
+++ b/src/optiontypes.cpp
@@ -0,0 +1,187 @@
+#include "optiontypes.h"
+
+
+/** @brief get the name of an entry type name
+  *
+  * This function returns a string, as it is most secure and
+  * only really needed for error and/or debugging messages
+  * where speed and efficiency are as unimportant as they
+  * can get.
+  *
+  * @param[in] etype The enum entry to get the name of.
+  * @return A string with the name or "UNIMPLEMENTED" if a new entry hasn't been
+  * added here, yet.
+  */
+std::string getEntryTypeName(eEntryType etype)
+{
+	switch (etype)
+	{
+		case ET_NONE:
+			return std::string("ET_NONE");
+			break;
+		case ET_ACTION:
+			return std::string("ET_ACTION");
+			break;
+		case ET_BUTTON:
+			return std::string("ET_BUTTON");
+			break;
+		case ET_COLOR:
+			return std::string("ET_COLOR");
+			break;
+		case ET_MENU:
+			return std::string("ET_MENU");
+			break;
+		case ET_OPTION:
+			return std::string("ET_OPTION");
+			break;
+		case ET_TEXT:
+			return std::string("ET_NONE");
+			break;
+		case ET_TOGGLE:
+			return std::string("ET_TOGGLE");
+			break;
+		case ET_VALUE:
+			return std::string("ET_VALUE");
+			break;
+		default:
+			break;
+	}
+	return std::string("UNIMPLEMENTED");
+}
+
+
+/** @brief get the name of menu class name
+  *
+  * This function returns a string, as it is most secure and
+  * only really needed for error and/or debugging messages
+  * where speed and efficiency are as unimportant as they
+  * can get.
+  *
+  * @param[in] mclass The enum entry to get the name of.
+  * @return A string with the name or "UNIMPLEMENTED" if a new entry hasn't been
+  * added here, yet.
+  */
+std::string getMenuClassName(eMenuClass mclass)
+{
+	switch(mclass)
+	{
+		case MC_FINANCE:
+			return std::string("MC_FINANCE");
+			break;
+		case MC_GRAPHICS:
+			return std::string("MC_GRAPHICS");
+			break;
+		case MC_MAIN:
+			return std::string("MC_MAIN");
+			break;
+		case MC_NETWORK:
+			return std::string("MC_NETWORK");
+			break;
+		case MC_PHYSICS:
+			return std::string("MC_PHYSICS");
+			break;
+		case MC_PLAY:
+			return std::string("MC_PLAY");
+			break;
+		case MC_PLAYERS:
+			return std::string("MC_PLAYERS");
+			break;
+		case MC_SOUND:
+			return std::string("MC_SOUND");
+			break;
+		case MC_WEATHER:
+			return std::string("MC_WEATHER");
+			break;
+		case MC_MENUCLASS_COUNT:
+			return std::string("MC_MENUCLASS_COUNT");
+			break;
+		default:
+			break;
+	}
+	return std::string("UNIMPLEMENTED");
+}
+
+
+/** @brief get the name of a text class name
+  *
+  * This function returns a string, as it is most secure and
+  * only really needed for error and/or debugging messages
+  * where speed and efficiency are as unimportant as they
+  * can get.
+  *
+  * @param[in] tclass The enum entry to get the name of.
+  * @return A string with the name or "UNIMPLEMENTED" if a new entry hasn't been
+  * added here, yet.
+  */
+std::string getTextClassName(eTextClass tclass)
+{
+	switch (tclass)
+	{
+		case TC_COLOUR:
+			return std::string("TC_COLOUR");
+			break;
+		case TC_LANDSLIDE:
+			return std::string("TC_LANDSLIDE");
+			break;
+		case TC_LANDTYPE:
+			return std::string("TC_LANDTYPE");
+			break;
+		case TC_LANGUAGE:
+			return std::string("TC_LANGUAGE");
+			break;
+		case TC_LIGHTNING:
+			return std::string("TC_LIGHTNING");
+			break;
+		case TC_METEOR:
+			return std::string("TC_METEOR");
+			break;
+		case TC_MOUSE:
+			return std::string("TC_MOUSE");
+			break;
+		case TC_OFFON:
+			return std::string("TC_OFFON");
+			break;
+		case TC_OFFONRANDOM:
+			return std::string("TC_OFFONRANDOM");
+			break;
+		case TC_PLAYERPREF:
+			return std::string("TC_PLAYERPREF");
+			break;
+		case TC_PLAYERTEAM:
+			return std::string("TC_PLAYERTEAM");
+			break;
+		case TC_PLAYERTYPE:
+			return std::string("TC_PLAYERTYPE");
+			break;
+		case TC_SATELLITE:
+			return std::string("TC_SATELLITE");
+			break;
+		case TC_SKIPTYPE:
+			return std::string("TC_SKIPTYPE");
+			break;
+		case TC_SOUNDDRIVER:
+			return std::string("TC_SOUNDDRIVER");
+			break;
+		case TC_TANKTYPE:
+			return std::string("TC_TANKTYPE");
+			break;
+		case TC_TURNTYPE:
+			return std::string("TC_TURNTYPE");
+			break;
+		case TC_WALLTYPE:
+			return std::string("TC_WALLTYPE");
+			break;
+		case TC_TEXTCLASS_COUNT:
+			return std::string("TC_TEXTCLASS_COUNT");
+			break;
+		case TC_FREETEXT:
+			return std::string("TC_FREETEXT");
+			break;
+		case TC_NONE:
+			return std::string("TC_NONE");
+			break;
+		default:
+			break;
+	}
+	return std::string("UNIMPLEMENTED");
+}
diff --git a/src/optiontypes.h b/src/optiontypes.h
new file mode 100644
index 0000000..41e6a33
--- /dev/null
+++ b/src/optiontypes.h
@@ -0,0 +1,128 @@
+#pragma once
+#ifndef ATANKS_SRC_OPTIONTYPES_H_INCLUDED
+#define ATANKS_SRC_OPTIONTYPES_H_INCLUDED
+
+/*
+ * atanks - obliterate each other with oversize weapons
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your menu) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+/** @file optiontypes.h
+  *
+  * This file defines enums used for the menus.
+**/
+
+
+#include <string>
+
+
+/** @enum eMenuClass
+  * @brief List of menu classes. Every menu class is a menu in itself.
+  *
+  * MC_MENUCLASS_COUNT can be used to retrieve the number of menu classes.
+  *
+  * This enum is sorted in alphabetical order to make maintenance easier.
+**/
+enum eMenuClass
+{
+	MC_AREYOUSURE = 0, //!< The "Are you sure?" <yes> <no> screen
+	MC_FINANCE,        //!< The finance ("Money") options sub menu
+	MC_GRAPHICS,       //!< The graphics options sub menu
+	MC_MAIN,           //!< Menu shown when using "Options" button
+	MC_NETWORK,        //!< The network options sub menu
+	MC_PHYSICS,        //!< The physics options sub menu
+	MC_PLAY,           //!< Menu shown when using "Play" button
+	MC_PLAYER,         //!< The player edit menu
+	MC_PLAYERS,        //!< Menu shown when hitting "Players" button
+	MC_RESET,          //!< The "Reset options?" <Reset> <Back> screen
+	MC_SOUND,          //!< The sound options sub menu
+	MC_WEATHER,        //!< The weather options sub menu
+	MC_MENUCLASS_COUNT
+};
+
+
+/** @enum eTextClass
+  * @brief Declare the menu option text classes.
+  *
+  * These are used so repeating texts do not need to be translated over and
+  * over again.
+  * TC_TEXTCLASS_COUNT can be used to retrieve the number of
+  * fixed menu entry text classes in OptionClassText[][][].
+  *
+  * This enum is sorted alphabetically to make maintenance easier.
+**/
+enum eTextClass
+{
+	TC_COLOUR = 0,
+	TC_LANDSLIDE,
+	TC_LANDTYPE,
+	TC_LANGUAGE,
+	TC_LIGHTNING,
+	TC_METEOR,
+	TC_MOUSE,
+	TC_OFFON,
+	TC_OFFONRANDOM,
+	TC_PLAYERPREF,
+	TC_PLAYERTEAM,
+	TC_PLAYERTYPE,
+	TC_SATELLITE,
+	TC_SKIPTYPE,
+	TC_SOUNDDRIVER,
+	TC_TANKTYPE,
+	TC_TURNTYPE,
+	TC_WALLTYPE,
+	TC_TEXTCLASS_COUNT, //!< First value after fixed text class list
+	TC_FREETEXT,        //!< Special value for dynamically composed text arrays
+	TC_NONE             //!< Special value for no text class at all
+};
+
+
+/** @enum eEntryType
+  * @brief Declare the different entry types option items can have
+**/
+enum eEntryType
+{
+	ET_NONE = 0,
+	ET_ACTION,
+	ET_BUTTON,
+	ET_COLOR,
+	ET_MENU,
+	ET_OPTION,
+	ET_TEXT,
+	ET_TOGGLE,
+	ET_VALUE
+};
+
+
+/** @enum eResetOptions
+  * @brief return codes for the "Are you sure" reset button question
+**/
+enum eResetOptions
+{
+	RO_BACK  = 667,
+	RO_RESET = 1337
+};
+
+
+// Some helper functions to get names for enum entries
+std::string getEntryTypeName(eEntryType etype);
+std::string getMenuClassName(eMenuClass mclass);
+std::string getTextClassName(eTextClass tclass);
+
+
+#endif // ATANKS_SRC_OPTIONTYPES_H_INCLUDED
+
diff --git a/src/perlin.cpp b/src/perlin.cpp
index ed08e9b..ca62feb 100644
--- a/src/perlin.cpp
+++ b/src/perlin.cpp
@@ -79,10 +79,10 @@ double interpolate (double x1, double x2, double i)
   double f = (1 - cos(ft)) * 0.5;
   double result = (x1 * (1 - f) + (x2 * f));
 
-  if (isnan(x1)||isnan(x2))
+  if (std::isnan(x1) || std::isnan(x2))
     return 0.0;
-  if (isnan(result))
-    return (x1+x2)/2.0; /* fall back to linear interpolation */
+  if (std::isnan(result))
+    return (x1 + x2) / 2.0; /* fall back to linear interpolation */
   return result;
 }
 
diff --git a/src/physobj.cpp b/src/physobj.cpp
index 695b95e..b5b7fae 100644
--- a/src/physobj.cpp
+++ b/src/physobj.cpp
@@ -20,231 +20,614 @@
 
 #include "physobj.h"
 #include "environment.h"
+#include "globaldata.h"
 
 
-void PHYSICAL_OBJECT::initialise ()
-    {
-      VIRTUAL_OBJECT::initialise ();
-      hitSomething = 0;
-    }
+PHYSICAL_OBJECT::PHYSICAL_OBJECT(bool is_weapon) :
+	VIRTUAL_OBJECT(),
+    isWeaponFire(is_weapon)
+{ /* nothing to do here */ }
 
 
-void PHYSICAL_OBJECT::draw (BITMAP *dest)
-    {
-      VIRTUAL_OBJECT::draw (dest);
-    }
+void PHYSICAL_OBJECT::initialise()
+{
+	VIRTUAL_OBJECT::initialise();
+	hitSomething = false;
+}
+
+
+/// @brief return true if this object was fired from a player weapon
+bool PHYSICAL_OBJECT::isWeapon()
+{
+	return isWeaponFire;
+}
 
 
+/// @brief get the current velocity. Only important for AICore to track clusters.
+void PHYSICAL_OBJECT::getVelocity(double &xv_, double &yv_)
+{
+	xv_ = xv;
+	yv_ = yv;
+}
 
-int  PHYSICAL_OBJECT::checkPixelsBetweenPrevAndNow ()
-    {
-      double startX = x - xv;
-      double startY = y - yv;
 
-      if (checkPixelsBetweenTwoPoints (_global, _env, &startX, &startY, x, y))
-        {
-          x = startX;
-          y = startY;
-          return (1);
-        }
+/// @brief check whether the object hit something and return true if it has
+/// Note: The objects x and y position is updated to the impact coordinates
+///       if it hit anything.
+bool PHYSICAL_OBJECT::checkPixelsBetweenPrevAndNow()
+{
+	double startX = x - xv;
+	double startY = y - yv;
 
-      return (0);
-    }
+	if (checkPixelsBetweenTwoPoints(&startX, &startY, x, y)) {
+		x = startX;
+		y = startY;
+		return true;
+	}
+
+	return false;
+}
 
 
 
 /** @brief applyPhysics
   *
-  * Moves the object according to momentum and wind, boundec off of walls/ceiling/floor, and checks
-  * whether something is hit.
+  * Moves the object according to momentum and wind, bounces off of
+  * walls/ceiling/floor, and checks whether something is hit.
+  *
+  * @return true if something was hit, false otherwise.
   */
-int PHYSICAL_OBJECT::applyPhysics()
+void PHYSICAL_OBJECT::applyPhysics()
 {
-  // Normal physics
-
-  double dPrevX = x;
-  double dPrevY = y;
-  if (((x + xv) <= 1) || ((x + xv) >= (_global->screenWidth - 1)))
-    {
-      if	(	(((x + xv) < 1) && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, 1, y))
-           ||(	((x + xv) > (_global->screenWidth - 2))
-               && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, (_global->screenWidth - 2), y))	)
-        {
-          x = dPrevX;
-          y = dPrevY;
-          hitSomething = true;
-        }
-      else
-        {
-          //motion - wind affected
-          switch (_env->current_wallType)
-            {
-            case WALL_RUBBER:
-              xv = -xv;	//bounce on the border
-              break;
-            case WALL_SPRING:
-              xv = -xv * SPRING_CHANGE;
-              break;
-            case WALL_WRAP:
-              if (xv < 0)
-                x = _global->screenWidth - 2; // -1 is the wall itself
-              else
-                x = 1;
-              break;
-            case WALL_STEEL:
-              x += xv;
-              if (x < 1)
-                x = 1;
-              if (x > (_global->screenWidth - 2))
-                x = _global->screenWidth - 2;
-              xv = 0; // already applied!
-              hitSomething = 1; // blow up on steel
-              break;
-            }
-        }
-    }
-  if (!hitSomething)
-    xv += (double)(_env->wind - xv) / mass * drag * _env->viscosity;
-
-  // hit floor or boxed top
-  if ((y + yv >= (_global->screenHeight - 1))
-      ||((y + yv <= MENUHEIGHT) && (_global->bIsBoxed)))
-    {
-      if	(	(	_global->bIsBoxed && ((y + yv) <= MENUHEIGHT)
-             && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, x, MENUHEIGHT + 1))
-           ||(	((y + yv) > (_global->screenHeight - 2))
-               && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, x, (_global->screenHeight - 2)))	)
-        {
-          x = dPrevX;
-          y = dPrevY;
-          hitSomething = true;
-        }
-      else
-        {
-          switch (_env->current_wallType)
-            {
-            case WALL_RUBBER:
-              yv = -yv * 0.5;
-              xv *= 0.95;
-              break;
-            case WALL_SPRING:
-              yv = -yv * SPRING_CHANGE;
-              xv *= 1.05;
-              break;
-            default:
-              // steel or wrap floor (ceiling)
-              y += yv;
-              if (y >= (_global->screenHeight - 1))
-                y = _global->screenHeight - 2; // -1 would be the floor itself!
-              else
-                y = MENUHEIGHT + 1; // +1 or it would be the wall itself
-              yv = 0; // already applied!
-              hitSomething = 1;
-            }
-          if (fabs(xv) + fabs(yv) < 0.8)
-            hitSomething = 1;
-        }
-    }
-
-  // velocity check:
-  /** the old calculation was wrong, but the new one, taking FPS into account, eventually works! **/
-  double dActVelocity = ABSDISTANCE((long double)xv, (long double)yv, 0, 0); // a²+b²=c² ... says Pythagoras :)
-  double dMaxVelocity = _global->dMaxVelocity * (1.20 + ((double)mass / ((double)MAX_POWER / 10.0)));
-  // apply mass, as more mass allows more max velocity:
-
-  // The default means, that a small missile can be accelerated by 25% over MAX_POWER,
-  // while a large Napalm Bomb can go up to 220%.
-
-  if (dActVelocity > dMaxVelocity)
-    {
-#ifdef DEBUG
-      cout << "** Missile too fast!" << endl;
-      cout << "mass: " << mass << " - Max Velocity: " << dMaxVelocity << " (FPS: " << _global->frames_per_second << ")" << endl;
-      cout << "xv: " << xv << " - yv: " << yv << " - Act Velocity: " << dActVelocity << endl;
-#endif // DEBUG
-      // apply *some* velocity, as the thing is killed on its way
-      y += yv / (1.0 + ((double)(rand() % 40) / 10.0)); // This produces somthing
-      x += xv / (1.0 + ((double)(rand() % 40) / 10.0)); // between 1.0 and 5.0
-      xv = 0.0;
-      yv = 0.0;
-      if (y <= MENUHEIGHT)
-        y = MENUHEIGHT + 1;
-      if (y > (_global->screenHeight - 2))
-        y = _global->screenHeight - 2;
-      if ((x < 1) && (_env->current_wallType != WALL_WRAP))
-        x = 1; // Wrapped Explosion takes care for explosions off the screen
-      if ((x > (_global->screenWidth - 2)) && (_env->current_wallType != WALL_WRAP))
-        x = _global->screenWidth - 2; // Wrapped Explosion takes care for explosions off the screen
-      hitSomething = 1;
-    }
-  if (!hitSomething)
-    {
-      x += xv;
-      y += yv;
-      yv += _env->gravity * (100.0 / _global->frames_per_second);
-      // Barrier test:
-      if ( (yv <= -1.0) && (y <= (_global->screenHeight * -25.0)))
-        yv *= -1.0;
-    }
-  else
-    {
-      // if something *is* hit, ensure that that damn thing is on screen! (only y matters here!)
-      if ((y < (MENUHEIGHT + 1)) && _global->bIsBoxed)	y = MENUHEIGHT + 1;
-      if ((y < MENUHEIGHT) && !_global->bIsBoxed)				y = MENUHEIGHT;
-    }
-  return (hitSomething);
+	// Apply wind to x-movement
+	xv += (global.wind - xv) / mass * drag * env.viscosity;
+
+	// Apply gravity to y movement
+	yv += env.gravity * env.FPS_mod;
+
+	// Barrier test:
+	if ( (yv <= -1.0) && (y <= (env.screenHeight * -25.0)))
+		yv *= -1.0;
+
+	bool isMoving = (std::abs(xv) + std::abs(yv)) < 0.01 ? false : true;
+
+	if (!isMoving)
+		return; // early out
+
+	/* There are 6 steps:
+	 * 1. Does the object hit a wall?
+	 * 2. Does it hit something else before reaching the wall?
+	 * 3. If nothing is hit, do the wall handling.
+	 * 4. If nothing is hit and if movement is left, determine movement delta
+	 *    and continue with 1.
+	 * 5. Does the object hit something on its way to its final destination?
+	 * 6. If nothing is hit, check the object velocity and detonate if too fast.
+	 */
+
+	// Shortcuts:
+	bool    hasTop   = env.isBoxed;
+	int32_t left     = 1;
+	int32_t right    = env.screenWidth - 2;
+	int32_t top      = MENUHEIGHT + (hasTop ? 1 : 0);
+	int32_t bottom   = env.screenHeight - 2;
+	double  xv_cur   = xv;
+	double  yv_cur   = yv;
+
+	// Special handling for Napalm Jellies if this is wrap or steel
+	// ceiling. They sort of 'glide off' of the ceiling instead of
+	// getting glued to it.
+	bool jelly = (NAPALM_JELLY == weapType)
+	          && ( (WALL_STEEL == env.current_wallType)
+	            || ( (WALL_WRAP  == env.current_wallType)
+	              && (!env.isBoxed || !env.do_box_wrap) ) )
+	           ? true : false;
+
+	// Easiest way is a loop that traces the path step-wise
+	while (isMoving && !hitSomething) {
+		double currX    = x;
+		double currY    = y;
+		double nextX    = x + xv_cur;
+		double nextY    = y + yv_cur;
+		double deltaX   = 0.;
+		double deltaY   = 0.;
+		double hitX     = 0.; // <0 = left, >0 = right
+		double hitY     = 0.; // <0 = top,  >0 = bottom
+		bool   hitWall  = false;
+		bool   hitFloor = false;
+		bool   hitLeft  = false; // Helper to not needing to check deltaX against 0.
+		bool   hitTop   = false; // Helper to not needing to check deltaY against 0.
+
+		// === 1.: Does the object hit a wall ? ===
+		// ========================================
+		if (nextX < left  ) {
+			deltaX  = left - x;
+			hitX    = deltaX / xv_cur;
+			hitWall = true;
+			hitLeft = true;
+		} else if (nextX > right ) {
+			deltaX  = right - x;
+			hitX    = deltaX / xv_cur;
+			hitWall = true;
+		}
+		if (nextY > bottom) {
+			deltaY   = bottom - y;
+			hitY     = deltaY / yv_cur;
+			hitFloor = true;
+		} else if (hasTop && (nextY < top) ) {
+			deltaY   = top - y;
+			hitY     = deltaY / yv_cur;
+			hitFloor = true;
+			hitTop   = true;
+		}
+
+		// Note: hit[XY] is now the percentage of the full movement to the hit.
+
+		if (hitWall || hitFloor) {
+			// === 2. Does it hit something else before reaching the wall? ===
+			// Note: This is just preparation, check 5 can do this once prepared.
+			// ===============================================================
+			if (!hitFloor || (hitWall && (std::abs(hitX) <= std::abs(hitY))) ) {
+				// The X-movement hits a wall earlier or at the same time (corner).
+				nextX    = hitLeft ? left : right;
+				deltaY   = yv_cur * hitX;
+				nextY    = y + deltaY;
+				hitFloor = false; // not reached
+			} else {
+				// The Y-movement hits bottom/top earlier
+				nextY   = hitTop ? top : bottom;
+				deltaX  = xv_cur * hitY;
+				nextX   = x + deltaX;
+				hitWall = false; // not reached
+				if (jelly && hitTop) {
+					nextY += 1.0;
+					yv      = static_cast<double>( (rand() % 10) + 1 ) / 25.00; // 0.04 - 0.40
+					xv     /= static_cast<double>( (rand() %  4) + 2 ) /  1.66; // 1.20 - 3.01
+				}
+			}
+			xv_cur -= deltaX;
+			yv_cur -= deltaY;
+		}
+
+		// === 5. Does the object hit something on its way to its final  ===
+		// ===    destination?                                           ===
+		// Note: This is done before the wall handling (step 3/4) as it also
+		//       terminates the movement towards a wall. Only if the path
+		//       really is clear wall handling makes sense.
+		// =================================================================
+		if (checkPixelsBetweenTwoPoints(&currX, &currY, nextX, nextY)) {
+			xv_cur = currX - nextX;
+			yv_cur = currY - nextY;
+			nextX  = currX;
+			nextY  = currY;
+			if (PT_DIRTBOUNCE == physType) {
+				double rxv, ryv;
+				getDirtBounceReact(nextX, nextY, xv, yv, rxv, ryv);
+
+				// Modify rxv/ryv, this is no full bounce:
+                if (std::abs(rxv) > std::abs(ryv)) {
+					rxv *= 0.66;
+					if (ryv < 0.)
+						ryv *= 0.5;
+                } else {
+					rxv *= 0.5;
+					if (ryv < 0.)
+						ryv *= 0.66;
+                }
+
+				// See how much of the current movement is left
+				double vel_rest = FABSDISTANCE2(xv_cur, yv_cur, 0., 0.)
+				                / FABSDISTANCE2(xv,     yv,     0., 0.);
+
+				// Now apply what is left:
+				xv_cur = rxv * vel_rest;
+				yv_cur = ryv * vel_rest;
+				xv = rxv;
+				yv = ryv;
+			} else {
+				hitSomething = true;
+				isMoving = false;
+			}
+
+			hitWall  = false;
+			hitFloor = false;
+		} else if (hitWall || hitFloor) {
+			// === 3. If nothing is hit, do the wall handling. ===
+			// ===================================================
+
+			// Note: Dirt bounce must be done first, it is handled
+			//       differently but for x-wrapping
+			if ( (PT_DIRTBOUNCE == physType)
+			  && ( (WALL_WRAP != env.current_wallType)
+				|| hitFloor) ) {
+				if (hitWall) {
+					xv_cur *= -0.5;
+					xv     *= -0.5;
+					if (yv < 0.) {
+						yv_cur *= 0.66;
+						yv     *= 0.66;
+					}
+				} else {
+					// Yes, dirt does not wrap though ceilings.
+					xv_cur *= 0.66;
+					xv     *= 0.66;
+					yv_cur *= nextY >= bottom ? -0.5 : -1.0;
+					yv     *= nextY >= bottom ? -0.5 : -1.0;
+				}
+			} else {
+				// count the bounce:
+				++bounces;
+
+				switch(env.current_wallType) {
+					case WALL_RUBBER:
+						if (hitWall) {
+							xv_cur  = -xv_cur * BOUNCE_CHANGE;
+							xv      = -xv     * BOUNCE_CHANGE;
+							yv_cur *= BOUNCE_CHANGE;
+							yv     *= BOUNCE_CHANGE;
+						} else {
+							yv_cur  = -yv_cur * BOUNCE_CHANGE;
+							yv      = -yv     * BOUNCE_CHANGE;
+							xv_cur *= BOUNCE_CHANGE;
+							xv     *= BOUNCE_CHANGE;
+						}
+						break;
+					case WALL_SPRING:
+						if (hitWall) {
+							xv_cur = -xv_cur * SPRING_CHANGE;
+							xv     = -xv     * SPRING_CHANGE;
+						} else {
+							yv_cur = -yv_cur * SPRING_CHANGE;
+							yv     = -yv     * SPRING_CHANGE;
+						}
+						break;
+					case WALL_WRAP:
+						if (hitWall) {
+							if (hitLeft) nextX = right;
+							else         nextX = left;
+						} else if (env.isBoxed && env.do_box_wrap) {
+							if (hitTop) {
+								// Some weapons do not warp through the
+								// ceiling if the bottom pixel is occupied
+								// and trigger instead:
+								int32_t bX = ROUNDu(nextX);
+								bool floor_free = global.surface[bX].load(ATOMIC_READ)
+								                >= bottom;
+								if (allowDirtyWrap || floor_free)
+									nextY = bottom;
+								else {
+									yv *= -1.;
+									hitSomething = true;
+								}
+							} else
+								nextY = top;
+						} else
+							hitSomething  = true;
+						break;
+					case WALL_STEEL:
+					default:
+						hitSomething = true;
+						break;
+				} // End of wall type switch
+			}
+
+		} // End of nothing hit, wall handling needed
+
+
+		// === 6. If nothing is hit, check the object velocity and ===
+		// ===    detonate if too fast. (depending on the mass)    ===
+		// ===========================================================
+		if (!hitSomething) {
+			double actVel = FABSDISTANCE2(xv_cur, yv_cur, 0, 0); // a²+b²=c² ... says Pythagoras :)
+			if ( (actVel > maxVel)
+			  || std::isinf(xv_cur) || std::isinf(yv_cur)
+			  || std::isinf(xv)     || std::isinf(yv)     ) {
+				// apply *some* velocity, as the thing is killed on its way
+				// (unless the current veocity is infinite of course
+				double velMod = 1.0 + ((double)(rand() % 40) / 10.0);
+				// This produces something between 1.0 and 5.0
+				if (!std::isinf(xv_cur))
+					nextX = x + (xv_cur / velMod);
+				if (!std::isinf(yv_cur))
+					nextY = y + (yv_cur / velMod);
+				xv = 0.0;
+				yv = 0.0;
+				if (nextY < top)
+					nextY = top;
+				else if (nextY > bottom)
+					nextY = bottom;
+				if (nextX < left) {
+					if (WALL_WRAP == env.current_wallType)
+						nextX = right - (static_cast<int32_t>(std::abs(nextX)) % right);
+					else
+						nextX = left;
+				} else if (nextX > right) {
+					if (WALL_WRAP == env.current_wallType)
+						nextX = static_cast<int32_t>(nextX) % right;
+					else
+						nextX = right;
+				}
+				hitSomething = true;
+				lacerated    = true; // oh dear...
+			}
+
+			// If the velocities were not only partly applied due to
+			// some wall/floor hit, all movement has been used up now.
+			if (!(hitWall || hitFloor))
+				isMoving = false;
+		} // End of velocity check
+
+		// === 4. If nothing is hit and if movement is left, check ===
+		// ===    remaining movement and prepare for 1. or exit    ===
+		// ===========================================================
+		if ( !hitSomething && isMoving && (hitWall || hitFloor)
+		  && ( (std::abs(xv) + std::abs(yv)) < 0.8) )
+			// If the movement has slowed down too much, take it as a hit
+			hitSomething = true;
+		else if (!hitSomething && isMoving && (hitWall || hitFloor)
+			  && ( (std::abs(xv_cur) + std::abs(yv_cur)) < 0.01) )
+			// Just stop, wall bouncing/wrapping didn't leave enough rest
+			isMoving = false;
+
+		// Finally set x/y
+		x = nextX;
+		y = nextY;
+	} // End of tracing the movement path
 }
 
-/* --- global method --- */
-int checkPixelsBetweenTwoPoints (GLOBALDATA *global, ENVIRONMENT *env, double *startX, double *startY, double endX, double endY)
+/* --- global function --- */
+bool checkPixelsBetweenTwoPoints (double *startX, double *startY, double endX, double endY)
 {
-  int landFound = 0;
-
-  // only check if on screen
-  double xDist = endX - *startX;
-  double yDist = endY - *startY;
-  double length;
-  double xInc;
-  double yInc;
-
-  if ((fabs(xDist) < 2) && (fabs(yDist) < 2))
-    {
-      if	(	(endX > 0) 													// 0 is the wall!
-           &&(endX < (global->screenWidth - 1))	// -1 is the wall!
-           &&(endY < (global->screenHeight - 1))	// -1 is the floor!
-           &&(endY > (MENUHEIGHT + 1))						// or it would always fail for high shots!
-           &&(getpixel (env->terrain, (int)endX, (int)endY) != PINK)	)
-        return (1);
-      else
-        return (0);
-    }
-
-  length = FABSDISTANCE(xDist, yDist, 0, 0);
-  yInc = yDist / length;
-  xInc = xDist / length;
-
-  // sanity check
-  if (length > (global->screenWidth + global->screenHeight))
-    length = global->screenWidth + global->screenHeight;
-
-  //check all pixels along line for land
-  for (int lengthPos = 0; lengthPos < length; lengthPos++)
-    {
-      //found land
-      if	(	(*startX > 0) 													// 0 is the wall!
-           &&(*startX < (global->screenWidth - 1))		// -1 is the wall!
-           &&(*startY < (global->screenHeight - 1))	// -1 is the floor!
-           &&(*startY > (MENUHEIGHT + 1))						// or it would always fail for high shots!
-           &&(getpixel(env->terrain, (int)*startX, (int)*startY) != PINK)	)
-        {
-          // Leaves startX and Y at current point if within range!
-          landFound = 1;
-          break;
-        }
-      *startX += xInc;
-      *startY += yInc;
-    }
-
-  return (landFound);
+	// return at once if there can't be any dirt in the box.
+	if (!global.isDirtInBox(*startX, *startY, endX, endY)) {
+		*startX = endX;
+		*startY = endY;
+		return false;
+	}
+
+	bool   result = false;
+	double xDist  = endX - *startX;
+	double yDist  = endY - *startY;
+	double length = FABSDISTANCE2(xDist, yDist, 0, 0);
+
+	// Shortcuts:
+	bool   hasTop = env.isBoxed;
+	double left   = 1;
+	double right  = env.screenWidth - 2;
+	double top    = MENUHEIGHT + (hasTop ? 1 : 0);
+	double bottom = env.screenHeight - 2;
+
+
+	// Drop out early if a neighbouring pixel is checked and it is a hit
+	if (length < 2.) {
+		if ( (endX > left)
+		  && (endX < right)
+		  && (endY > top)
+		  && (endY < bottom) ) {
+
+			if (PINK != getpixel(global.terrain, endX, endY) ) {
+				*startX = endX;
+				*startY = endY;
+				result  = true;
+			}
+
+		} // End of having a valid position
+		return result;
+	} // End of early drop out
+
+	// Otherwise the path must be checked
+	double xInc = xDist / length;
+	double yInc = yDist / length;
+
+	// sanity check
+	if (length > (env.screenWidth + env.screenHeight))
+		length = env.screenWidth + env.screenHeight;
+
+	// check all pixels along the line for land
+
+	// As xInc/yInc are known now, left, right, top and bottom can
+	// be corrected if the line would not leave the screen.
+	left   = std::min(std::min(*startX,   left), *startX + (length * xInc));
+	top    = std::min(std::min(*startY,    top), *startY + (length * yInc));
+	right  = std::max(std::max(*startX,  right), *startX + (length * xInc));
+	bottom = std::max(std::max(*startY, bottom), *startY + (length * yInc));
+
+	// Note: Start with 1 and increase startX/Y first, as
+	//       the starting pixel can be assumed to be clean.
+	for (int32_t pos = 1; !result && (pos < length); ++pos) {
+		*startX += xInc;
+		*startY += yInc;
+
+		if ( (*startX > left)
+		  && (*startX < right)
+		  && (*startY > top)
+		  && (*startY < bottom) ) {
+
+			if (PINK != getpixel (global.terrain, *startX, *startY) )
+				result = true;
+				// Note: startX/startY now point to the hit pixel
+
+		} // End of having a valid position
+	} // End of walking positions
+
+	// If nothing was hit, make sure startX/Y point to endX/Y
+	if (!result) {
+		*startX = endX;
+		*startY = endY;
+	}
+
+	return result;
 }
 
+/** @brief Return the reaction velocity values of an object that hits dirt
+  *
+  * The function analyses the pixels around @a x and @a y to find
+  * a plane the vector @a xv / @a yv has an angle to and returns
+  * appropriate reaction velocity values in @a rxv and @a ryv.
+**/
+void getDirtBounceReact(int32_t x, int32_t y, double xv, double yv,
+						double &rxv, double &ryv)
+{
+    int32_t from_x    = xv < 0. ? 1 : -1;
+    double  vel       = FABSDISTANCE2(xv, yv, 0., 0.);
+
+	// First find the heights around x/y:
+    int32_t y_map[5];
+    for (int i = 0 ; i < 5 ; ++i) {
+		int32_t xpos = x + (i - 2);
+		int32_t min_y = y - 2;
+		int32_t max_y = y + 2;
+		int32_t start_y = std::max(min_y, MENUHEIGHT);
+		int32_t stop_y  = std::min(max_y + 1, env.screenHeight);
+		y_map[i] = -1;
+
+		if ( (xpos > 0) && (xpos < env.screenWidth)
+		  && (min_y < env.screenHeight) && (max_y > MENUHEIGHT)
+		  && ( (stop_y - start_y) > 0) ) {
+			y_map[i] = 5;
+
+			for (int32_t j = 4; (j >= 0) && (5 == y_map[i]); --j) {
+				int32_t ypos = y + (j - 2);
+
+				if ( (ypos <  start_y)
+				  || (ypos >= stop_y)
+				  || (PINK != getpixel(global.terrain, xpos, ypos)) )
+					y_map[i] = j;
+			}
+		}
+    } // End of generating height map.
+
+	/* atan2(x, y) will be used which results in the following angles:
+	 * Only right  1/ 0 :   90 (270) (The number in brackets is the normalized
+	 * Only left  -1/ 0 : - 89 ( 91)  angle used in atanks)
+	 * Only down   0/ 1 :    0 (180)
+	 * Only up     0/-1 :  180 (360)
+	 * Down right  1/ 1 :   45 (225)
+	 * Up   right  1/-1 :  135 (315)
+	 * Down left  -1/ 1 : - 44 (136)
+	 * Up   left  -1/-1 : -134 ( 46)
+	 *
+	 * The plane is calculated using the same x direction if present.
+	 * If the plane is a vertical wall, only the x-movement is reversed using
+	 * a plane angle of 0.
+	 *
+	 * HA = Hit Angle, PA = Plane Angle, RA = Reaction Angle
+	 *
+	 * 1: HA = PA - Movement angle
+	 * 2: RA = PA + (PA - HA)
+	 * 3: Values >  180 gets 360 substracted
+	 * 4: Value  < -180 gets 360 added
+	 *
+	 * Examples:
+	 * 1: Shot from left to right and down (down right = 1/1 = 45°)
+	 *    Plane is horizontal (only right = 1/0 = 90°)
+	 *   HA = 90 - 45        =  45
+	 *   RA = 90 + (90 - 45) = 135 (Up right)
+	 * 2: Shot is the same, but the plane goes up right (1/-1 = 135°)
+	 *   HA = 135 - 45         = 90
+	 *   RA = 135 + (135 - 45) = 225 => 225 - 360 = -135 (Up left)
+	 * 3: Shot from right to left and down (down left = -1/1 = -45)
+	 *    Plane is horizontal (only left = -1/0 = -90°)
+	 *   HA = -90 - -45         = -45
+	 *   RA = -90 + (-90 - -45) = -135 (Up left)
+	 * 4: Shot from the right bottom (up-left (-1/-1) = -135)
+	 *    Plane is a vertical wall (only down = 0)
+	 *   HA = 0 - -135 = 135 = RA
+	 * 5: Shot from right to left down (-1/1) = -40
+	 *    Plane up left (-1/-1) = -135
+	 *   HA = -135 - -40 = -95
+	 *   RA = -135 + (-135 - -95) = -175
+	 */
+
+	// Find a plane with angle using the height map
+	int32_t MA = GET_ANGLE(xv, yv);
+	int32_t PA = 0, HA = 0, RA = 0;
+
+	// Look for the special case of a vertical wall first:
+	if ( (y_map[2] < 2) && (y_map[2 + from_x] > 3) )
+		if (MA)
+			RA = 0 - MA;
+		else
+			// Just let it drip off
+			RA = 5 * (y_map[3] ? 1 : -1);
+	else {
+		// Here a plane must be determined.
+		double x1 = 2., y1 = y_map[2];
+		double x2 = 2., y2 = y_map[2];
+		double p1 = 1., p2 = 1.;
+
+		// If y2 is 0, no further look is needed into movement direction
+		if (y2 > 0.) {
+			// Ah, it is.
+			int32_t ly1 = y_map[2 + (-1 * from_x)];
+			int32_t ly2 = y_map[2 + (-2 * from_x)];
+
+			if (ly1 > -1) {
+				x2 += 2. + (-1. * from_x);
+				y2 += ly1;
+				p2 += 1.;
+
+				// Must ly2 be considered?
+				if (ly2 > -1) {
+					if ( ( (ly1 <= y_map[2]) && (ly2 <= ly1) )
+					  || ( (ly1 >= y_map[2]) && (ly2 >= ly1) ) ) {
+						x2 += 2. + (-2. * from_x);
+						y2 += ly2;
+						p2 += 1.;
+					}
+				} // End of having a non-wall ly2
+
+				// Set final x2/y2:
+				x2 /= p2;
+				y2 /= p2;
+			} // End of having a non-wall ly1
+		} // end of having a non-vertical hit plane.
+
+		// For the 'from'-direction the already set y2 can be used
+		int32_t ly1 = y_map[2 +      from_x];
+		int32_t ly2 = y_map[2 + (2 * from_x)];
+
+		if ( (ly1 > -1)
+		  && ( ( (ly1 >= y_map[2])
+			  && (ly1 >= y2) )
+			|| ( (ly1 <= y_map[2])
+			  && (ly1 <= y2) ) ) ) {
+			x1 += 2. + (1. * from_x);
+			y1 += ly1;
+			p1 += 1.;
+
+			// Only continue if useful.
+			if (ly2 > -1) {
+				if ( ( (ly1 <= y_map[2]) && (ly2 <= ly1) )
+				  || ( (ly1 >= y_map[2]) && (ly2 >= ly1) ) ) {
+					x1 += 2. + (2. * from_x);
+					y1 += ly2;
+					p1 += 1.;
+				}
+			} // end of having a non-wall ly2
+
+			// Set final x1/y1:
+			x1 /= p1;
+			y1 /= p1;
+		} // End of having a non-wall ly1 without contra-slope.
+
+		// Set resulting angles:
+		PA = GET_ANGLE(x2 - x1, y2 - y1);
+		HA = PA - MA;
+		RA = PA + (PA - HA);
+
+		// Secure against vertical drop traps:
+		if (!MA && !PA && !HA)
+			// RA is now 0 but must be 180
+			RA = 180;
+
+		if (RA >  180) RA -= 360;
+		if (RA < -180) RA += 360;
+	} // End of plane determination
+
+	// The [R]eaction [A]angle now has to be translated into
+	// atanks compatible velocity values:
+	if (RA < 0) RA += 360; // atanks range
+
+	rxv = env.slope[RA][0] * vel;
+	ryv = env.slope[RA][1] * vel;
+}
diff --git a/src/physobj.h b/src/physobj.h
index 32cfd72..06c8551 100644
--- a/src/physobj.h
+++ b/src/physobj.h
@@ -20,40 +20,103 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  * */
 
-
-#include "main.h"
+#include "globaltypes.h"
 #include "virtobj.h"
-#include "globaldata.h"
-//#include "environment.h"
+
+// Switch sides (real angle!)
+#define FLIP_ANGLE(angle_) (180 + (180 - (angle_)))
+
+// Get the direct angle without any checks
+#define GET_ANGLE(x,y) ([](double a, double b)->int32_t { \
+	double result = RAD2DEG(std::atan2(a, b)); \
+	/* atan2 returns an angle with 180° up, 90° right */ \
+	/* and -90° left. But we need it from 90° right to */ \
+	/* 270° left counter-clockwise. */ \
+	if (result < 0) result += 360.; \
+	return ROUND(result); \
+}(static_cast<double>(x), static_cast<double>(y)))
+
+
+// Get the angle brought into the 90-270 degree range
+// To be usable more widely, this macro allows an additional
+// argument "m", which is the angle modifier (errors made
+// by the AI and such things)
+#define GET_SAFE_ANGLE(x,y,m) ([](double a, double b, double c)->int32_t { \
+	double result = RAD2DEG(std::atan2(a, b)) + c; \
+	if      (result <   0.) result += 360.; \
+	if      (result <  90.) result  =  90.; \
+	else if (result > 270.) result  = 270.; \
+	return ROUND(result); \
+}(static_cast<double>(x), static_cast<double>(y), static_cast<double>(m)))
+
+// Re-calculate angle_ into a value displayable on the top bar:
+#define GET_DISP_ANGLE(angle_) (180 - ((angle_) - 90))
+
 
 class PHYSICAL_OBJECT: public VIRTUAL_OBJECT
-  {
-  public:
-    int	noimpact;
-    int	hitSomething;
-    double	mass;
-    double	drag;
-    int	spin;
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+	explicit PHYSICAL_OBJECT(bool is_weapon);
+	// No explicit dtor needed
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	virtual void draw () _PURE;
+	void getVelocity(double &xv_, double &yv_);
+	bool isWeapon();
+
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+	bool    allowDirtyWrap = true; //!< Whether ceiling wrap is allowed into dirt bottom
+	double  drag           = 0.;
+	bool    hitSomething   = false;
+	int32_t	weapType       = 0;
+
+protected:
 
-    /* --- constructor --- */
-    PHYSICAL_OBJECT ():VIRTUAL_OBJECT(),hitSomething(0) { }
+	/* -------------------------
+	 * --- Protected methods ---
+	 * -------------------------
+	 */
 
-    /* --- pure virtual methods --- */
-    virtual int	isSubClass (int classNum)_PURE;
-    virtual int	getClass ()_PURE;
+	void applyPhysics                 ();
+	bool checkPixelsBetweenPrevAndNow ();
+	void initialise                   ();
 
-    /* --- non-virtual methods --- */
 
-    /* --- inline methods --- */
-    int	applyPhysics ();
+	/* -------------------------
+	 * --- Protected members ---
+	 * -------------------------
+	 */
 
+	int32_t bounces      = 0;     //!< Bounces off walls, floor and ceiling
+	bool    isWeaponFire = true;
+	bool    lacerated    = false; //!< Set to true if the velocity check fails.
+	double  mass         = 0.;
+	double  maxVel       = 0.;    //!< maximum Velocity
+	bool    noimpact     = false;
+	int32_t spin         = 0;
 
-    int	checkPixelsBetweenPrevAndNow ();
+};
 
-    void	initialise ();
 
-    void	draw (BITMAP *dest);
-  };
+/// global helper methods:
+bool checkPixelsBetweenTwoPoints(double *startX, double *startY,
+								double endX, double endY);
+void getDirtBounceReact(int32_t x, int32_t y, double xv, double yv,
+						double &rxv, double &ryv);
 
 #endif
 
diff --git a/src/player.cpp b/src/player.cpp
index bbcf37f..573a609 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -26,4521 +26,940 @@
 #include "files.h"
 #include "floattext.h"
 #include "network.h"
+#include "missile.h"
+#include "aicore.h"
 
-// When defined draws AI 'planning'
-//#define AI_PLANNING_DEBUG	1
+#include <cassert>
 
-PLAYER::PLAYER (GLOBALDATA *global, ENVIRONMENT *env):_global(global),_env(env),_turnStage(0),_target(NULL),
-    _oldTarget(NULL),iTargettingRound(0),revenge(NULL),tank(NULL),pColor(NULL),menuopts(NULL),menudesc(NULL)
-{
-  int my_colour;
-
-  money = 15000;
-  score = 0;
-  played = 0;
-  won = 0;
-  tank = NULL;
-  team = TEAM_NEUTRAL;
-
-  iBoostItemsBought = -1;
-
-  selected = FALSE;
-  changed_weapon = false;
-
-
-  tank_bitmap = 0.0;
-
-  nm[0] = 99;
-  for (int count = 1; count < WEAPONS; count++)
-    nm[count] = 0;
-
-  for (int count = 0; count < ITEMS; count++)
-    ni[count] = 0;
-
-  strncpy (_name, "New", NAME_LENGTH);
-  type = HUMAN_PLAYER;
-
-  // 25% of time set to perplay weapon preferences
-  preftype = (rand () % 4)?ALWAYS_PREF:PERPLAY_PREF;
-//  generatePreferences (); <-- not here!
-
-  defensive = (double)((rand () % 10000) - 5000) / 5000.0;
-  vengeful = rand () % 100;
-  vengeanceThreshold = (double)(rand () % 1000) / 1000.0;
-  revenge = NULL;
-  /** @TODO: These two values are nowhere used.
-    * We should think about a usage, because bot characteristics could become
-    * alot more wide spread with their help. **/
-  selfPreservation = (double)(rand () % 3000) / 1000;
-  painSensitivity = (double)(rand () % 3000) / 1000;
-
-  // color = rand () % WHITE;
-  my_colour = rand() % 4;
-  switch (my_colour)
-    {
-    case 0:
-      color = makecol(200 + (rand() % 56), rand() % 25, rand() % 25);
-      break;
-    case 1:
-      color = makecol( rand() % 25, 200 + (rand() % 56), rand() % 25);
-      break;
-    case 2:
-      color = makecol( rand() % 25, rand() % 25, 200 + (rand() % 56) );
-      break;
-    case 3:
-      color = makecol( 200 + (rand() % 56), rand() % 25, 200 + (rand() % 56));
-      break;
-    }
-  typeText[0] = global->ingame->complete_text[54];
-  typeText[1] = global->ingame->complete_text[55];
-  typeText[2] = global->ingame->complete_text[56];
-  typeText[3] = global->ingame->complete_text[57];
-  typeText[4] = global->ingame->complete_text[58];
-  typeText[5] = global->ingame->complete_text[59];
-  preftypeText[0] = global->ingame->complete_text[60];
-  preftypeText[1] = global->ingame->complete_text[61];
-  tank_type[0] = global->ingame->complete_text[62];
-  tank_type[1] = global->ingame->complete_text[63];
-  tank_type[2] = global->ingame->complete_text[64];
-  tank_type[3] = global->ingame->complete_text[65];
-  tank_type[4] = global->ingame->complete_text[73];
-  tank_type[5] = global->ingame->complete_text[74];
-  tank_type[6] = global->ingame->complete_text[75];
-  tank_type[7] = global->ingame->complete_text[76];
-  teamText[0] = global->ingame->complete_text[66];
-  teamText[1] = global->ingame->complete_text[67];
-  teamText[2] = global->ingame->complete_text[68];
-
-  // Weapon Preferences need to be initalized!
-  for (int weapCount = 0; weapCount < THINGS; weapCount++)
-    _weaponPreference[weapCount] = 0;
-
-  menudesc = NULL;
-  menuopts = NULL;
-  initMenuDesc ();
-  skip_me = false;
-  #ifdef NETWORK
-  server_socket = 0;
-  #endif
-}
-
-/*
-PLAYER::PLAYER (GLOBALDATA *global, ENVIRONMENT *env, ifstream &ifsFile, bool file):_global(global),_env(env),
-    _target(NULL),revenge(NULL),tank(NULL),pColor(NULL),menuopts(NULL),menudesc(NULL)
-{
-  money = 15000;
-  score = 0;
-  _turnStage = 0;
-  selected = FALSE;
-
-  tank_bitmap = 0.0;
-  team = TEAM_NEUTRAL;
-
-  iBoostItemsBought = -1;
-
-  nm[0] = 99;
-  for (int count = 1; count < WEAPONS; count++)
-    nm[count] = 0;
-
-  for (int count = 0; count < ITEMS; count++)
-    ni[count] = 0;
-
-  if (file)
-    loadFromFile(ifsFile);
-
-  type = (int)type;
-
-  typeText[0] = "Human";
-  typeText[1] = "Useless";
-  typeText[2] = "Guesser";
-  typeText[3] = "Ranger";
-  typeText[4] = "Targetter";
-  typeText[5] = "Deadly";
-  preftypeText[0] = "Per Game";
-  preftypeText[1] = "Only Once";
-  tank_type[0] = "Normal";
-  tank_type[1] = "Classic";
-  tank_type[2] = "Big Grey";
-  tank_type[3] = "T34";
-  teamText[0] = "Sith";
-  teamText[1] = "Neutral";
-  teamText[2] = "Jedi";
-  initMenuDesc ();
-  skip_me = false;
-  #ifdef NETWORK
-  net_command[0] = '\0';
-  #endif
-}
-*/
-
-
-int PLAYER::getBoostValue()
-{
-      return ((int)(ni[ITEM_ARMOUR] * ((double) item[ITEM_ARMOUR].vals[0] / (double) item[ITEM_PLASTEEL].vals[0])) + ni[ITEM_PLASTEEL] +
-              (int)(ni[ITEM_INTENSITY_AMP] * ((double) item[ITEM_INTENSITY_AMP].vals[0] / (double) item[ITEM_VIOLENT_FORCE].vals[0])) +
-              ni[ITEM_VIOLENT_FORCE]);
-}
-
-
-
-void PLAYER::setComputerValues (int aOffset)
-{
-  int iType = (int) type + aOffset;
-  if (iType > (int) DEADLY_PLAYER)
-    iType	=	(int) DEADLY_PLAYER;
-  rangeFindAttempts	=	(int)(pow(iType + 1, 2) + 1);				// Useless: 5  , Deadly: 37
-  retargetAttempts  = (int)(pow(iType, 2) + 1);						// Useless: 2  , Deadly: 26
-  focusRate         = ( (double) iType * 2.0) / 10.0;     // Useless: 0.2, Deadly:  1.0
-  errorMultiplier	=	(double)(DEADLY_PLAYER + 1 - iType) / (double)rangeFindAttempts;
-  if (errorMultiplier > 2.0) errorMultiplier	= 2.0;
-}
-
-int displayPlayerName (GLOBALDATA *global, ENVIRONMENT *env, int x, int y, void *data);
-void PLAYER::initMenuDesc ()
-{
- GLOBALDATA *global = _global;
-  int destroyPlayer (GLOBALDATA *global, ENVIRONMENT *env, void *data);
-  int i = 0;
-  int height = -68;
-
-  // before we get started, eraw any old version of the menu
-  if (menuopts)
-     free(menuopts);
-  if (menudesc)
-     free(menudesc);
-
-  // menudesc = new MENUDESC;
-  menudesc = (MENUDESC *) calloc(1, sizeof(MENUDESC));
-  if (!menudesc)
-    {
-      perror ( "player.cc: Failed allocating memory for menudesc in PLAYER::initMenuDesc");
-      return;
-      // exit (1);
-    }
-  menudesc->title = _name;
-
-  // Name,Color
-  menudesc->numEntries = 10;
-  menudesc->okayButton = TRUE;
-  menudesc->quitButton = FALSE;
-
-  // menuopts = new MENUENTRY[menudesc->numEntries];
-  menuopts = (MENUENTRY *) calloc(menudesc->numEntries, sizeof(MENUENTRY));
-  if (!menuopts)
-    {
-      perror ( "player.cc: Failed allocating memory for menuopts in PLAYER::initMenuDesc");
-      return;
-      // exit (1);
-    }
-
-  //init memory
-  // memset(menuopts, 0, menudesc->numEntries * sizeof(MENUENTRY));
-
-  // Player name
-  menuopts[i].name = global->ingame->complete_text[29];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = color;
-  menuopts[i].value = (double*)_name;
-  menuopts[i].specialOpts = NULL;
-  menuopts[i].type = OPTION_TEXTTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  // Player colour
-  menuopts[i].name = global->ingame->complete_text[30];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  pColor = &color;
-  menuopts[i].value = (double*) pColor;
-  menuopts[i].specialOpts = NULL;
-  menuopts[i].type = OPTION_COLORTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  // Player type (human, computer)
-  menuopts[i].name = global->ingame->complete_text[31];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = (double*)&type;
-  menuopts[i].min = 0;
-  menuopts[i].max = LAST_PLAYER_TYPE - 1;
-  menuopts[i].increment = 1;
-  menuopts[i].defaultv = 0;
-  menuopts[i].format = "%s";
-  menuopts[i].specialOpts = typeText;
-  menuopts[i].type = OPTION_SPECIALTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  menuopts[i].name = global->ingame->complete_text[20];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = (double *)&team;
-  menuopts[i].min = 0;
-  menuopts[i].max = TEAM_JEDI;
-  menuopts[i].increment = 1;
-  menuopts[i].defaultv = TEAM_NEUTRAL;
-  menuopts[i].format = "%s";
-  menuopts[i].specialOpts = teamText;
-  menuopts[i].type = OPTION_SPECIALTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  // Player preftype (human, computer)
-  menuopts[i].name = global->ingame->complete_text[32];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = (double*)&preftype;
-  menuopts[i].min = 0;
-  menuopts[i].max = ALWAYS_PREF;
-  menuopts[i].increment = 1;
-  menuopts[i].defaultv = 0;
-  menuopts[i].format = "%s";
-  menuopts[i].specialOpts = preftypeText;
-  menuopts[i].type = OPTION_SPECIALTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  menuopts[i].name = global->ingame->complete_text[33];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = (double*)&played;
-  menuopts[i].format = "%.0f";
-  menuopts[i].specialOpts = NULL;
-  menuopts[i].type = OPTION_DOUBLETYPE;
-  menuopts[i].viewonly = TRUE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  menuopts[i].name = global->ingame->complete_text[34];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = (double*)&won;
-  menuopts[i].format = "%.0f";
-  menuopts[i].specialOpts = NULL;
-  menuopts[i].type = OPTION_DOUBLETYPE;
-  menuopts[i].viewonly = TRUE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  menuopts[i].name = global->ingame->complete_text[35];
-  menuopts[i].displayFunc = Display_Tank_Bitmap;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = &tank_bitmap;
-  menuopts[i].min = 0;
-  menuopts[i].max = TANK_TYPES;
-  menuopts[i].increment = 1;
-  menuopts[i].defaultv = 0;
-  menuopts[i].format = "%1.0f";
-  menuopts[i].specialOpts = tank_type;
-  menuopts[i].type = OPTION_SPECIALTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  menuopts[i].name = global->ingame->complete_text[36];
-  menuopts[i].displayFunc = NULL;
-  menuopts[i].color = WHITE;
-  menuopts[i].value = (double*)destroyPlayer;
-  menuopts[i].data = (void*)this;
-  menuopts[i].type = OPTION_ACTIONTYPE;
-  menuopts[i].viewonly = FALSE;
-  menuopts[i].x = _global->halfWidth - 3;
-  menuopts[i].y = _global->halfHeight + height;
-  i++;
-  height += 20;
-
-  menudesc->entries = menuopts;
-}
-
-PLAYER::~PLAYER ()
-{
-  if (tank)
-    delete(tank);
-
-  if (menuopts)
-     free(menuopts);
-    // delete(menuopts);
-  if (menudesc)
-    free(menudesc);
-    // delete(menudesc);
-
-  _global = NULL;
-  _env    = NULL;
-  _target = NULL;
-  revenge = NULL;
-
-  pColor = NULL;
-}
-
-
-
-
-/*
-Save the player settings in text form. Fields are
-formateed as
-[name]=[value]\n
-
-Function returns true on success and false on failure.
-*/
-int PLAYER::saveToFile_Text (FILE *file)
-{
-  int count;
-
-  if (! file) return FALSE;
-  // start section with (char *)"*PLAYER*"
-  fprintf (file, "*PLAYER*\n");
-  fprintf (file, "NAME=%s\n", _name);
-  fprintf (file, "VENGEFUL=%d\n", vengeful);
-  fprintf (file, "VENGEANCETHRESHOLD=%lf\n", vengeanceThreshold);
-  fprintf (file, "TYPE=%lf\n", type);
-  fprintf (file, "TYPESAVED=%lf\n", type_saved);
-  fprintf (file, "COLOR=%d\n", color);
-  fprintf (file, "COLOR2=%d\n", color2);
-  for (count = 0; count < THINGS; count++)
-    fprintf (file, "WEAPONPREFERENCES=%d %d\n", count, _weaponPreference[count]);
-
-  fprintf (file, "PLAYED=%lf\n", played);
-  fprintf (file, "WON=%lf\n", won);
-  fprintf (file, "PREFTYPE=%lf\n", preftype);
-  fprintf (file, "SELFPRESERVATION=%lf\n", selfPreservation);
-  fprintf (file, "PAINSENSITIVITY=%lf\n", painSensitivity);
-  fprintf (file, "DEFENSIVE=%lf\n", defensive);
-  fprintf (file, "TANK_BITMAP=%lf\n", tank_bitmap);
-  fprintf (file, "TEAM=%lf\n", team);
-  fprintf (file, "***\n");
-  return TRUE;
-}
-
-
-/*
-This function tries to load player data from a text file.
-Each line is parsed for (char *)"field=value", except WEAPONPREFERENCES
-which is parsed (char *)"field=index value".
-If all goes well TRUE is returned, on error the function returns FALSE.
--- Jesse
-*/
-
-int PLAYER::loadFromFile_Text (FILE *file)
-{
-  char line[MAX_CONFIG_LINE];
-  int equal_position, line_length;
-  int index, wp_value;
-  char field[MAX_CONFIG_LINE], value[MAX_CONFIG_LINE];
-  char *result = NULL;
-  bool done = false;
-
-  if (! file) return FALSE;
-
-  // read until we hit line (char *)"*PLAYER*" or "***" or EOF
-  do
-    {
-      result = fgets(line, MAX_CONFIG_LINE, file);
-      if (! result)     // eof
-        return FALSE;
-       if (! strncmp(line, "***", 3) )     // end of record
-         return FALSE;
-    }
-  while ( strncmp(line, "*PLAYER*", 8) );     // read until we hit new record
-
-  while ( (result) && (!done) )
-    {
-      // read a line
-      memset(line, '\0', MAX_CONFIG_LINE);
-      result = fgets(line, MAX_CONFIG_LINE, file);
-      if (result)
-        {
-          // if we hit end of the record, stop
-          if (! strncmp(line, "***", 3) ) return TRUE;
-          // find equal sign
-         line_length = strlen(line);
-          // strip newline character
-          if ( line[line_length - 1] == '\n')
-            {
-              line[line_length - 1] = '\0';
-              line_length--;
-            }
-          equal_position = 1;
-          while ( ( equal_position < line_length) && (line[equal_position] != '=') )
-           equal_position++;
-          // make sure we have valid equal sign
-          if (equal_position <= line_length)
-            {
-              // seperate field from value
-              memset(field, '\0', MAX_CONFIG_LINE);
-              memset(value, '\0', MAX_CONFIG_LINE);
-              strncpy(field, line, equal_position);
-              strcpy(value, & (line[equal_position + 1]));
-              // check which field we have and process value
-              if (! strcasecmp(field, "name") )
-                strncpy(_name, value, NAME_LENGTH);
-              else if (! strcasecmp(field, "vengeful") )
-                sscanf(value, "%d", &vengeful);
-              else if (! strcasecmp(field, "vengeancethreshold") )
-                sscanf(value, "%lf", &vengeanceThreshold);
-              else if (! strcasecmp(field, "type") )
-                sscanf(value, "%lf", &type);
-              else if (! strcasecmp(field, "typesaved") )
-                {
-                  sscanf(value, "%lf", &type_saved);
-                  if (type_saved > HUMAN_PLAYER)
-                    type = type_saved;
-                }
-              else if (! strcasecmp(field, "color") )
-                sscanf(value, "%d", &color);
-              else if (! strcasecmp(field, "color2") )
-                sscanf(value, "%d", &color2);
-              else if (! strcasecmp(field, "played") )
-                sscanf(value, "%lf", &played);
-              else if (! strcasecmp(field, "won") )
-                sscanf(value, "%lf", &won);
-              else if (! strcasecmp(field, "preftype") )
-                sscanf(value, "%lf", &preftype);
-              else if (! strcasecmp(field, "selfpreservation") )
-                sscanf(value, "%lf", &selfPreservation);
-              else if (! strcasecmp(field, "painsensititveity") )
-                sscanf(value, "%lf", &painSensitivity);
-              else if (! strcasecmp(field, "defensive") )
-                sscanf(value, "%lf", &defensive);
-              else if (! strcasecmp(field, "tank_bitmap") )
-                sscanf(value, "%lf", &tank_bitmap);
-              else if (! strcasecmp(field, "team") )
-                sscanf(value, "%lf", &team);
-              else if (! strcasecmp(field, "weaponpreferences") )
-                {
-                  sscanf(value, "%d %d", &index, &wp_value);
-                  if ( (index < THINGS) && (index >= 0) )
-                    _weaponPreference[index] = wp_value;
-                }
-
-            }    // end of valid data line
-        }      // end of if we read a line properly
-    }   // end of while not done
-
-  // make sure previous human players are restored as humans
-  if (type == PART_TIME_BOT)
-    type = HUMAN_PLAYER;
-
-  return TRUE;
-}
-
-
-
-
-void PLAYER::exitShop ()
-{
-  double tmpDM = 0;
-
-  damageMultiplier = 1.0;
-  tmpDM += ni[ITEM_INTENSITY_AMP] * item[ITEM_INTENSITY_AMP].vals[0];
-  tmpDM += ni[ITEM_VIOLENT_FORCE] * item[ITEM_VIOLENT_FORCE].vals[0];
-  if (tmpDM > 0)
-    damageMultiplier += pow (tmpDM, 0.6);
-}
-
-
-// run this at the begining of each turn
-void PLAYER::newRound ()
-{
-  // if the player is under computer control, give it back to the player
-  if ( type == PART_TIME_BOT )
-    type = HUMAN_PLAYER;
-
-  setComputerValues ();
-
-  if (!tank)
-    {
-      tank = new TANK(_global, _env);
-      if (tank)
-      {
-        tank->player = this;
-        tank->initialise();
-      }
-      else
-          perror ( "player.cc: Failed allocating memory for tank in PLAYER::newRound");
-    }
-  // newRound() doesn't need to be called, because ENVIRONMENT::newRound() has already done that!
-
-  changed_weapon = false;
-  // if we are playing in a campaign, raise the AI level
-  if (_global->campaign_mode)
-    {
-      if ( (type > HUMAN_PLAYER) && (type < DEADLY_PLAYER) )
-        type += 1.0;
-    }
-
-  // forget revenge under certain circumstances
-  if (revenge)
-    {
-      if ((team != TEAM_NEUTRAL) && (team == revenge->team))
-        revenge = NULL; // No more round breaking revenge on team mates!
-      else if (  (team == TEAM_NEUTRAL)
-                 ||((team != TEAM_NEUTRAL) && (revenge->team == TEAM_NEUTRAL)))
-        {
-          // neutral to !neutral and vice versa might forget...
-          if (  (!(rand() % (int)labs((type + 3) / 2)))
-                ||((rand() % 100) > vengeful) )
-            revenge = NULL;
-          /* This gives:
-           * USELESS: (1 + 3) / 2) = 2 => 50%
-           * GUESSER: (2 + 3) / 2) = 2 => 50%
-           * RANGEFI: (3 + 3) / 2) = 3 => 34%
-           * TARGETT: (4 + 3) / 2) = 3 => 34%
-           * DEADLY : (5 + 3) / 2) = 4 => 25%
-           * chance to "forgive". Should be okay...
-           * The check against "vengeful" makes "peacefull" bots to forget more easily
-           */
-        }
-    }
-
-  if (!revenge && (rand() % ((int)type + 1)))
-    {
-      // If there is no revengee there is a small chance the player will seek the leader!
-      int iMaxScore = score;
-      int iCurScore = 0;
-      for (int i = 0; i < _global->numPlayers; i++)
-        {
-          if (_global->players[i])
-            {
-              iCurScore = _global->players[i]->score;
-              if (  (iCurScore > iMaxScore)
-                    &&( (team == TEAM_NEUTRAL)
-                        ||(team != _global->players[i]->team)) )
-                {
-                  // Higher score found, record as possible revengee
-                  iMaxScore = iCurScore;
-                  if (abs(iMaxScore - score) > (int)type)
-                    revenge = _global->players[i];
-                }
-            }
-        }
-    }
-
-  time_left_to_fire = (int) _global->max_fire_time;
-  skip_me = false;
-  iTargettingRound = 0;
-  _target = NULL;
-  _oldTarget = NULL;
-  last_shield_used = 0;
-}
-
-
-void PLAYER::initialise ()
-{
-  long int totalPrefs;
-  int rouletteCount;
-
-  nm[0] = 99;
-  for (int count = 1; count < WEAPONS; count++)
-    nm[count] = 0;
-
-  for (int count = 0; count < ITEMS; count++)
-    ni[count] = 0;
-
-  totalPrefs = 0;
-  for (int weapCount = 0; weapCount < THINGS; weapCount++)
-    totalPrefs += _weaponPreference[weapCount];
-
-  rouletteCount = 0;
-  for (int weapCount = 0; weapCount < THINGS; weapCount++)
-    {
-      int weapRSpace = (int)((double)_weaponPreference[weapCount] / totalPrefs * NUM_ROULETTE_SLOTS);
-      int weapRCount = 0;
-
-      if (weapRSpace < 1)
-        weapRSpace = 1;
-      while (weapRCount < weapRSpace && rouletteCount + weapRCount < NUM_ROULETTE_SLOTS)
-        {
-          _rouletteWheel[rouletteCount + weapRCount] = weapCount;
-          weapRCount++;
-        }
-      rouletteCount += weapRSpace;
-    }
-  while (rouletteCount < NUM_ROULETTE_SLOTS)
-    _rouletteWheel[rouletteCount++] = rand () % THINGS;
-
-  kills       = 0;
-  killed       = 0;
-  tank        = NULL;
-  _target     = NULL;
-  _oldTarget  = NULL;
-}
-
-void PLAYER::generatePreferences ()
-{
-  double dBaseProb	=	(double) MAX_WEAP_PROBABILITY / 2.0;
-  int currItem	=	0;
-  double dWorth;
-  int iValue;
-  bool	bIsWarhead	= false;
-  int iMaxWeapPref = 0;
-  int iMaxItemPref = 0;
-
-  defensive	=	(2.0 * ( (double) rand () / (double) RAND_MAX)) - 1.0;
-  double dDefenseMod = (defensive * -1.0) + 2.0;
-  // DefenseMod will be between 1.0 (defensive) and 3.0 (offensive)
-  // and is used to modifiy vengeful, vengeanceThreshold, selfPreservation and painSensitivity
-  vengeful *= 1.0 + (dDefenseMod / 5.0); // +0% - +60% (defensive - offensive)
-  if (vengeful > 100) vengeful = 100;
-  vengeanceThreshold *= 1.0 - (dDefenseMod / 5.0); // -0% - -60%
-  selfPreservation /= dDefenseMod;
-  painSensitivity  /= dDefenseMod;
-
-  // Now defensive can be modified by team:
-  if (team == TEAM_JEDI)
-    {
-      defensive += (double)((double)(rand() % 1000)) / 1000.0;
-      if (defensive > 1.25)
-        defensive = 1.25; // + 1.25 is SuperDefensive
-    }
-  if (team == TEAM_SITH)
-    {
-      defensive -= (double)((double)(rand() % 1000)) / 1000.0;
-      if (defensive < -1.25)
-        defensive = -1.25; // - 1.25 is SuperAggressive
-    }
-#ifdef DEBUG
-  cout << "Generating Preferences for \"" << getName() << "\" (" << defensive << ")" << endl;
-  cout << "----------------------------------------------------" << endl;
-#endif // DEBUG
-  _weaponPreference[0] = 0; // small missiles are always zero!
-  for (int weapCount = 1; weapCount < THINGS; weapCount++)
-    {
-      dWorth			=	-1.0 * ( (double) MAX_WEAP_PROBABILITY / 4.0);
-      bIsWarhead	=	false;
-      if (weapCount < WEAPONS)
-        {
-          // Talking about weapons
-          currItem	=	weapCount;
-          if (weapon[currItem].warhead || ( (currItem >= SML_METEOR) && (currItem <= LRG_LIGHTNING)))
-            bIsWarhead	=	true; // Bots don't think about warheads or environment!
-          else
-            {
-              // 1. Damage:
-              int iWarheads = weapon[currItem].spread; // For non-spread this is always 1
-              if (weapon[currItem].numSubmunitions > 0)
-                {
-                  iWarheads	=	weapon[currItem].numSubmunitions;	// It's a cluster
-
-                  dWorth		= weapon[weapon[currItem].submunition].damage * iWarheads; // total damage for clusters
-
-                  if	( ( (currItem >= SML_NAPALM) && (currItem <= LRG_NAPALM))
-                       || ( (currItem >= FUNKY_BOMB) && (currItem <= FUNKY_DEATH)))
-                    dWorth /= defensive + 1.0 + ( (double) type / 2.0);
-                  // These weapons are too unpredictable to be counted full
-                  // But a true offensive useless bot devides only by 1.0 (not all all, doesn't mind)
-                  // And a true defensive deadly bot devides by 5.0
-                  if (dWorth > dBaseProb) dWorth = dBaseProb; // Or Large Napalm will always be everybodys favorite
-                }
-              else
-                dWorth			=	weapon[currItem].damage * (iWarheads * 2.0);	// total damage for (non-)spreads
-              // Note: iWarheads is counted twice, because otherwise spread weapons get far too low score!
-              dWorth = dWorth * (dBaseProb * 0.0005); // 1 Damage is worth 0.5%o of dBaseProb
-
-              // 2. Defensiveness multiplier
-              // As said above, defensive players avoid spread/cluster weapons. Thus they rate non-spreads higher:
-              if (iWarheads == 1)
-                dWorth *= (defensive + 1.5) * ( (double) type / 2.0);
-              else
-                dWorth -= (defensive * ( (double) type / 2.0)) * dWorth;
-
-              // 3. Dirtballs do no damage and have to be rated by defensiveness
-              if ( (currItem >= DIRT_BALL) && (currItem <= SUP_DIRT_BALL))
-                dWorth = (double) (currItem - (double) DIRT_BALL + 1.0) * 150.0 * ( (double) type / 2.0) * (defensive + 2.0);
-
-              // 4.: Shaped charges, wide boys and cutters are deadly but limited:
-              if ( (currItem	>= SHAPED_CHARGE) && (currItem <= CUTTER))
-                dWorth *= 1.0 - ( ( (double) type + (defensive * 5.0)) / 20.0);
-              // useless, full offensive: * 1.20
-              // deadly, full defensive : * 0.50
-
-              // 5.: rollers and penetrators are modified by type, as they *are* usefull
-              if	(	((currItem >= SML_ROLLER) && (currItem <= DTH_ROLLER))
-                   ||((currItem >= BURROWER) && (currItem <= PENETRATOR)))
-                dWorth *= 1.0 + ((double)type / 10.0) + (defensive  / 2);
-
-              // 6.: Tectonis need to be raised!
-              if ((currItem >= TREMOR) && (currItem <= TECTONIC))
-                dWorth *= 2.0 + ((double)type / 10.0) + (defensive  / 2);
-
-              // finally dWorth must not be greater than the 3/4 of MAX_WEAPON_PROBABILITY
-              if (dWorth > (MAX_WEAP_PROBABILITY * 0.75))
-                dWorth	=	MAX_WEAP_PROBABILITY * 0.75;
-            }
-        }
-      else
-        {
-          // Talking about items
-          currItem	=	weapCount - WEAPONS;
-          // unfortunately we can only switch here...
-          /* Theory:
-          		As for armour/amps/shields, offensive bots go for amps and reflector
-          		shields, defensive bots go for armour and hard shields.	*/
-          switch (currItem)
-            {
-            case ITEM_TELEPORT:
-              dWorth = -1.0 * ((defensive - 1.5) * ((double)MAX_WEAP_PROBABILITY / 10.0));
-              break;
-            case ITEM_SWAPPER:
-              dWorth = -1.0 * ((defensive - 1.5) * ((double)MAX_WEAP_PROBABILITY / 7.5));
-              break;
-            case ITEM_FAN:
-              dWorth = 0.0; // useless things!
-              break;
-            case ITEM_VENGEANCE:
-            case ITEM_DYING_WRATH:
-            case ITEM_FATAL_FURY:
-              dWorth = (defensive + 1.5) * ((double)weapon[(int)item[currItem].vals[0]].damage * (double)item[currItem].vals[1]);
-              break;
-            case ITEM_ARMOUR:
-            case ITEM_PLASTEEL:
-              dWorth	=	dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_PLASTEEL].vals[0]);
-              dWorth	*=	defensive;
-              break;
-            case ITEM_LGT_SHIELD:
-            case ITEM_MED_SHIELD:
-            case ITEM_HVY_SHIELD:
-              dWorth	=	dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_HVY_SHIELD].vals[0]);
-              dWorth	*=	defensive;
-              break;
-            case ITEM_INTENSITY_AMP:
-            case ITEM_VIOLENT_FORCE:
-              dWorth	=	dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_VIOLENT_FORCE].vals[0]);
-              dWorth	*=	(-1.0 * defensive);
-              break;
-            case ITEM_LGT_REPULSOR_SHIELD:
-            case ITEM_MED_REPULSOR_SHIELD:
-            case ITEM_HVY_REPULSOR_SHIELD:
-              dWorth	=	dBaseProb * ( (double) item[currItem].vals[0] / (double) item[ITEM_HVY_REPULSOR_SHIELD].vals[0]);
-              dWorth	*=	(-1.0 * defensive);
-              break;
-            case ITEM_REPAIRKIT:
-              dWorth	=	dBaseProb * ( (defensive + 1.0) / 2.0);
-              break;
-            case ITEM_PARACHUTE:
-              dWorth	=	dBaseProb * ( (defensive + 1.0) / 1.5);	// Parachutes *are* popular! :)
-              break;
-            case ITEM_SLICKP:
-              dWorth	=	(int) type * 250;
-              break;
-            case ITEM_DIMPLEP:
-              dWorth	=	(int) type * 500;
-              break;
-            case ITEM_FUEL:
-              dWorth	=	-5000;	// Bots don't need  fuel
-              bIsWarhead	=	true;	// Yes, it's a lie. ;-)
-            }
-          // dWorth must not be greater than the half of MAX_WEAPON_PROBABILITY
-          if (dWorth > (MAX_WEAP_PROBABILITY / 2))
-            dWorth	=	MAX_WEAP_PROBABILITY / 2;
-        }
-      iValue	=	fabs (dWorth);
-      if (iValue < (MAX_WEAP_PROBABILITY / 10)) iValue = MAX_WEAP_PROBABILITY / 10;
-      dWorth += (double) (rand() % iValue); // allow to double (more or less)
-
-      if (dWorth	>	MAX_WEAP_PROBABILITY)
-        dWorth	=	MAX_WEAP_PROBABILITY;
-      if (dWorth	<	(MAX_WEAP_PROBABILITY / 100.0))
-        dWorth	=	MAX_WEAP_PROBABILITY / 100.0; // Which is very very little...
-
-      if (bIsWarhead)
-        _weaponPreference[weapCount] = 0;	// It will not get any slot!
-      else
-        _weaponPreference[weapCount] = (int) dWorth;
-
-      if ((weapCount  < WEAPONS) && (_weaponPreference[weapCount] > iMaxWeapPref))
-        iMaxWeapPref = _weaponPreference[weapCount];
-      if ((weapCount >= WEAPONS) && (_weaponPreference[weapCount] > iMaxItemPref))
-        iMaxItemPref = _weaponPreference[weapCount];
-
-#ifdef DEBUG
-      if (weapCount < WEAPONS)
-        printf( "%23s (weapon): % 5d", weapon[weapCount].name, _weaponPreference[weapCount]);
-      else
-        printf( "%23s ( item ): % 5d", item[weapCount-WEAPONS].name, _weaponPreference[weapCount]);
-      cout << endl;
-#endif // DEBUG
-    }
-
-  // Before we are finished, we need to amplify the preferences (well, maybe...)
-  if (iMaxWeapPref < MAX_WEAP_PROBABILITY)
-    {
-      // Yes, amplification for the weapons needed!
-      dWorth = (double)MAX_WEAP_PROBABILITY / (double)iMaxWeapPref;
-      for (int weapCount = 1; weapCount < WEAPONS; weapCount++)
-        {
-          if (_weaponPreference[weapCount] > (MAX_WEAP_PROBABILITY / 100.0))
-            {
-              _weaponPreference[weapCount] = (int)((double)_weaponPreference[weapCount] * dWorth);
-#ifdef DEBUG
-              printf( "%23s (weapon) amplified to: % 5d", weapon[weapCount].name, _weaponPreference[weapCount]);
-              cout << endl;
-#endif // DEBUG
-            }
-        }
-    }
-  if (iMaxItemPref < MAX_WEAP_PROBABILITY)
-    {
-      // Yes, amplification for the items needed!
-      dWorth = (double)MAX_WEAP_PROBABILITY / (double)iMaxItemPref;
-      for (int weapCount = WEAPONS; weapCount < THINGS; weapCount++)
-        {
-          if (_weaponPreference[weapCount] > (MAX_WEAP_PROBABILITY / 100.0))
-            {
-              _weaponPreference[weapCount] = (int)((double)_weaponPreference[weapCount] * dWorth);
-#ifdef DEBUG
-              printf( "%23s ( item ) amplified to: % 5d", item[weapCount-WEAPONS].name, _weaponPreference[weapCount]);
-              cout << endl;
-#endif // DEBUG
-            }
-        }
-    }
-
-#ifdef DEBUG
-  cout << "===================================================" << endl << endl;
-#endif // DEBUG
-}
-
-int PLAYER::selectRandomItem ()
-{
-  // return (_rouletteWheel[rand () % NUM_ROULETTE_SLOTS]);
-  return rand() % THINGS;
-}
-
-void PLAYER::setName (char *name)
-{
-  // initalize name
-  memset(_name, '\0', NAME_LENGTH);
-  strncpy (_name, name, NAME_LENGTH - 1);
-}
-
-int PLAYER::controlTank ()
-{
-  if (key[KEY_F1])
-    save_bmp ( "scrnshot.bmp", _env->db, NULL);
-
-  if (key_shifts & KB_CTRL_FLAG && ctrlUsedUp)
-    {
-      if (key[KEY_LEFT] || key[KEY_RIGHT] ||
-          key[KEY_UP] || key[KEY_DOWN] ||
-          key[KEY_PGUP] || key[KEY_PGDN] ||
-          key[KEY_A] || key[KEY_D] ||         //additional control
-          key[KEY_W] || key[KEY_S] ||
-          key[KEY_R] || key[KEY_F])
-        ctrlUsedUp = TRUE;
-      else
-        ctrlUsedUp = FALSE;
-    }
-  else
-    {
-      ctrlUsedUp = FALSE;
-    }
-
-  if (_global->computerPlayersOnly &&
-      ((int)_global->skipComputerPlay >= SKIP_HUMANS_DEAD))
-    {
-      if (_env->stage == STAGE_ENDGAME)
-        return (-1);
-    }
-
-  k = 0;
-  #ifdef NEW_GAMELOOP
-  if ( keypressed() )
-  #else
-  if (keypressed () && !fi)
-  #endif
-    {
-      k = readkey ();
-
-      if ((_env->stage == STAGE_ENDGAME) &&
-          (k >> 8 == KEY_ENTER ||
-           k >> 8 == KEY_ESC ||
-           k >> 8 == KEY_SPACE))
-        return (-1);
-      if ( (k >> 8 == KEY_ESC) || (k >> 8 == KEY_P) )
-        {
-          void clockadd ();
-          install_int_ex (clockadd, SECS_TO_TIMER(6000));
-          int mm = _env->ingamemenu ();
-          install_int_ex(clockadd, BPS_TO_TIMER(_global->frames_per_second));
-          _env->make_update (0, 0, _global->screenWidth, _global->screenHeight);
-          _env->make_bgupdate (0, 0, _global->screenWidth, _global->screenHeight);
-
-          //Main Menu
-          if (mm == 1)
-          {
-              _global->wr_lock_command();
-              _global->command = GLOBAL_COMMAND_MENU;
-              _global->unlock_command();
-              return (-1);
-          }
-          else if (mm == 2)  //Quit game
-          {
-              _global->wr_lock_command();
-              _global->command = GLOBAL_COMMAND_QUIT;
-              _global->unlock_command();
-              return (-1);
-          }
-          else if (mm == 3)   // skip AI
-          {
-              return (-2);
-          }
-        }
-      // check for number key being pressed
-      if ( (k >> 8 >= KEY_0) && (k >> 8 <= KEY_9) )
-        {
-          int value = (k >> 8) - KEY_0;
-
-          // make sure the value is within range
-          if (value < _global->numPlayers)
-            {
-              if ( _global->players[value] )
-                {
-                  TANK *my_tank = _global->players[value]->tank;
-                  if (my_tank)
-                    {
-                      sprintf(_global->tank_status, "%s: %d + %d -- Team: %s", _global->players[value]->_name, my_tank->l, my_tank->sh, _global->players[value]->Get_Team_Name() );
-                      /* We do this in atanks.cpp, to kill this wretched "No Format Error"
-                      strcat(_global->tank_status, _global->players[value]->Get_Team_Name()); */
-                      _global->tank_status_colour = _global->players[value]->color;
-                      _global->updateMenu = 1;
-                    }
-                  else
-                    _global->tank_status[0] = 0;
-                }
-            }
-
-        }    // end of check status keys
-      if ( (k & 0xff) == 'v' )
-        _env->decreaseVolume();
-      else if ( (k & 0xff) == 'V' )
-        _env->increaseVolume();
-    }
-
-  if ((int)type == HUMAN_PLAYER || !tank)
-  {
-      return (humanControls ());
-  }
-  #ifdef NETWORK
-  else if ((int) type == NETWORK_CLIENT)
-      return (Execute_Network_Command(TRUE));
-  #endif
-  else if (_env->stage == STAGE_AIM)
-  {
-      return (computerControls ());
-  }
-  return (0);
-}
-
-int PLAYER::computerSelectPreBuyItem (int aMaxBoostValue)
-{
-  double dMood = 1.0 + defensive + (double) ( (double) (rand() / ( (double) RAND_MAX / 2.0)));
-  // dMood is 0.0 <= x <= 4.0
-  int currItem = 0;
-  /*	Prior buying anything else, a 5 step system takes place:
-  		1.: Parachutes (if gravity is on)
-  		2.: Minimum weapon probability (aka 5 medium and 3 large missiles
-  		3.: Armor/Amps
-  		4.: "Tools" to free themselves like Riot Blasts
-  		5.: Shields, if enough money is there
-  		6.: if all is set, look for dimpled/slick projectiles! */
-
-  // Step 1:
-
-  if	( (type >= RANGEFINDER_PLAYER)
-       && (_env->landSlideType > LANDSLIDE_NONE)
-       && (ni[ITEM_PARACHUTE] < 10))
-    currItem = WEAPONS + ITEM_PARACHUTE;
-
-  // Step 3:
-  // Even here bots might forget
-  if	(!currItem && (rand() % ( (int) type + 1)))
-    {
-      int iLimit = aMaxBoostValue - getBoostValue(); // > 0 means: Someone has more than we have!
-#ifdef DEBUG
-      printf( "%10s: Boost: %4d, Max: %4d, Limit: %4d\n", getName(), getBoostValue(), aMaxBoostValue, iLimit);
-#endif // DEBUG
-      if ( ((dMood >= 2.75) && (iLimit > 0)) || ((dMood >= 2.0) && (iLimit > getBoostValue())) )
-        {
-          // The player is in a defensive mood
-          // If we have 25% more money than the plasteel cost, buy it, else the armour will do
-          if (money >= (item[ITEM_PLASTEEL].cost * 1.25))
-            currItem = WEAPONS + ITEM_PLASTEEL;
-          else
-            if	( (money >= (item[ITEM_ARMOUR].cost * 2.0))
-                 && ( (ni[ITEM_ARMOUR] < ni[ITEM_PLASTEEL])
-                      || (dMood >= 3.5)))
-              currItem = WEAPONS + ITEM_ARMOUR;
-        }
-
-      // Now iBoostItemsBought must be checked:
-      if (currItem && (iBoostItemsBought >= (int)type))
-        currItem = 0; // Sorry, bought enough this round!
-      if (currItem && (iBoostItemsBought < (int)type))
-        iBoostItemsBought++; // Okay, take it!
-    }
-
-  // 5.: Shields
-  if (!currItem && (rand() % ( (int) type + 1)) && ( (int) type >= RANGEFINDER_PLAYER))
-    {
-      if (dMood <= 1.5)
-        {
-          // offensive type, go through reflectors
-          if ( (ni[ITEM_LGT_REPULSOR_SHIELD] <= (item[ITEM_LGT_REPULSOR_SHIELD].amt * (int) type))
-               && (money >= (item[ITEM_LGT_REPULSOR_SHIELD].cost * 2.0)))
-            currItem	=	WEAPONS + ITEM_LGT_REPULSOR_SHIELD;
-
-          if ( (ni[ITEM_MED_REPULSOR_SHIELD] <= (item[ITEM_MED_REPULSOR_SHIELD].amt * (int) type))
-               && (money >= (item[ITEM_MED_REPULSOR_SHIELD].cost * 1.75)))
-            currItem	=	WEAPONS + ITEM_MED_REPULSOR_SHIELD;
-
-          if ( (ni[ITEM_HVY_REPULSOR_SHIELD] <= (item[ITEM_HVY_REPULSOR_SHIELD].amt * (int) type))
-               && (money >= (item[ITEM_HVY_REPULSOR_SHIELD].cost * 1.5)))
-            currItem	=	WEAPONS + ITEM_HVY_REPULSOR_SHIELD;
-        }
-
-      if (dMood >= 2.5)
-        {
-          // defensive type, go through hard shields
-          if ( (ni[ITEM_LGT_SHIELD] <= (item[ITEM_LGT_SHIELD].amt * (int) type))
-               && (money >= (item[ITEM_LGT_SHIELD].cost * 2.0)))
-            currItem	=	WEAPONS + ITEM_LGT_SHIELD;
-
-          if ( (ni[ITEM_MED_SHIELD] <= (item[ITEM_MED_SHIELD].amt * (int) type))
-               && (money >= (item[ITEM_MED_SHIELD].cost * 1.75)))
-            currItem	=	WEAPONS + ITEM_MED_SHIELD;
-
-          if ( (ni[ITEM_HVY_SHIELD] <= (item[ITEM_HVY_SHIELD].amt * (int) type))
-               && (money >= (item[ITEM_HVY_SHIELD].cost * 1.5)))
-            currItem	=	WEAPONS + ITEM_HVY_SHIELD;
-        }
-    }
-
-  return (currItem);
-}
-
-int PLAYER::chooseItemToBuy (int aMaxBoostValue)
-{
-  int currItem = computerSelectPreBuyItem (aMaxBoostValue);
-  int itemNum = 0;
-  int cumulative;
-  int nextMod, curramt, newamt;
-  int iTRIES = THINGS; // pow((int)type + 1, 2);
-  int iDesiredItems[THINGS]; // Deadly bots have large shopping carts. ;)
-  bool bIsSorted = false; // Whether the cart is sorted or not
-  bool bIsPreSelected = currItem?true:false;	// Whether or not the PreBuy steps found something
-  int i = 0;
-
-  if (currItem)
-  {
-       if ( Buy_Something(currItem) )
-         return currItem;
-  }
-
-  // init desired items
-  for (i = 0; i < iTRIES; i++)
-     iDesiredItems[i] = 0;
- 
-  // 1.: Fill cart
-  i = 0;
-  while (i < iTRIES)
-    {
-      currItem = (int) fabs (selectRandomItem());
-      if (currItem >= THINGS)
-        currItem	%=	THINGS;	// Put in range
-      // now currItem is 0<= currItem < THINGS
-      if ( (_env->isItemAvailable (currItem)) && (currItem != 0))
-        {
-          iDesiredItems[i]	=	currItem;
-        }
-        i++;
-    }
-
-  // 2.: sort these items by preferences
-  while (!bIsSorted)
-    {
-      if (bIsPreSelected)
-        i = 2; // The first item shall not be sorted somewhere else!
-      else
-        i = 1;
-      bIsSorted	=	true;
-      while (i < iTRIES)
-        {
-          if (_weaponPreference[iDesiredItems[i-1]] < _weaponPreference[iDesiredItems[i]])
-            {
-              bIsSorted	=	false;
-              currItem	=	iDesiredItems[i];
-              iDesiredItems[i]	=	iDesiredItems[i-1];
-              iDesiredItems[i-1] =	currItem;
-            }
-          i++;
-        }
-    }
-
-  // 3.: loop through all weapon preferences
-  for (int i = 0; i < iTRIES; i++)
-    {
-      currItem	=	iDesiredItems[i];
-      itemNum = currItem - WEAPONS;
-      //determine the likelyhood of purchasing this selection
-      //less likely the more of the item is owned
-      //if have zero of item, it is a fifty/fifty chance of purchase
-      if (currItem < WEAPONS)
-        {
-          curramt = nm[currItem];
-          newamt = weapon[currItem].amt;
-          cumulative = FALSE;
-        }
-      else
-        {
-          curramt = ni[itemNum];
-          newamt = item[itemNum].amt;
-          if ( (itemNum >= ITEM_INTENSITY_AMP &&
-                itemNum <= ITEM_VIOLENT_FORCE) ||
-               (itemNum == ITEM_REPAIRKIT) ||
-               (itemNum >= ITEM_ARMOUR &&
-                itemNum <= ITEM_PLASTEEL))
-            cumulative = TRUE;
-          else
-            cumulative = FALSE;
-        }
-      nextMod = 1;
-      if (!cumulative)
-        nextMod = curramt / newamt;
-      if (nextMod <= 0)
-        nextMod = 1;
-      if (rand () % nextMod)
-        continue;
-
-      //weapon
-      if (currItem < WEAPONS)
-        {
-          //don't buy if already maxed out
-          if (nm[currItem] >= 99)
-            continue;
-
-          //purchase the item
-          if (money >= weapon[currItem].cost)
-            {
-              money -= weapon[currItem].cost;
-              nm[currItem] += weapon[currItem].amt;
-              //don't allow more than 99
-              if (nm[currItem] > 99)
-                nm[currItem] = 99;
-              return currItem;
-            }
-        }
-      else   //item
-        {
-          //don't buy if already maxed out
-          if (ni[itemNum] >= 99)
-            continue;
-          //purchase the item
-          if (money >= item[itemNum].cost)
-            {
-              // Check against iBoostItemsBought
-              if ( (itemNum >= ITEM_INTENSITY_AMP &&
-                    itemNum <= ITEM_VIOLENT_FORCE) ||
-                   (itemNum >= ITEM_ARMOUR &&
-                    itemNum <= ITEM_PLASTEEL))
-                {
-                  if ((iBoostItemsBought >= (int)type)
-                      ||(getBoostValue() > aMaxBoostValue))
-                    continue; // no chance pal!
-                  else
-                    iBoostItemsBought++; // Okay, take it!
-                }
-              money -= item[itemNum].cost;
-              ni[itemNum] += item[itemNum].amt;
-              //don't allow more than 999
-              if (ni[itemNum] > 99)
-                ni[itemNum] = 99;
-              return (currItem);
-            }
-        }
-    }
-  return (-1);
-}
-
-
-
-// An item has been selected, this function merely buys it. It
-// first does checks to make sure the item can be bought.
-// The function returns TRUE if we successfuly bought the item or
-// FALSE if we could not get it for some reason.
-int PLAYER::Buy_Something(int currItem)
-{
-   int itemNum = currItem - WEAPONS;
-   int bought = FALSE;
-
-    if (currItem < WEAPONS)
-    {
-        if ( (money >= weapon[currItem].cost) && (nm[currItem] < 99) )
-        {
-             money -= weapon[currItem].cost;
-             nm[currItem] += weapon[currItem].amt;
-             //don't allow more than 99
-             if (nm[currItem] > 99)
-                nm[currItem] = 99;
-             bought = TRUE;
-        }
-        // check tech level
-        if (weapon[currItem].techLevel <= _env->weapontechLevel)
-           bought = TRUE;
-        else
-           bought = FALSE;
-       
-    }    // end of weapons
-
-    else   // item
-    { 
-      if ( (money > item[itemNum].cost) && (ni[itemNum] < 99) )
-      {
-         money -= item[itemNum].cost;
-         ni[itemNum] += item[itemNum].amt;
-         // don't allow more than 99
-         if (ni[itemNum] > 99)
-            ni[itemNum] = 99;
-         bought = TRUE;
-      }
-      // check technology level
-      if (item[itemNum].techLevel <= _env->itemtechLevel)
-          bought = TRUE;
-      else
-          bought = FALSE;
-    }    
-   return bought;
-}
-
-
-
-char *PLAYER::selectRevengePhrase ()
-{
-  char *line;
-
-  line = _global->revenge->Get_Random_Line();
-  return line;
-}
-
-char *PLAYER::selectGloatPhrase ()
-{
-  char *line;
-  line = _global->gloat->Get_Random_Line();
-  return line;
-}
-
-char *PLAYER::selectSuicidePhrase ()
-{
-  char *line;
-  line = _global->suicide->Get_Random_Line();
-  return line;
-}
-
-
-char *PLAYER::selectKamikazePhrase ()
-{
-  char *line;
-  line = _global->kamikaze->Get_Random_Line();
-  return line;
-}
-
-char *PLAYER::selectRetaliationPhrase ()
-{
-  char *line;
-  char *pText;
-
-  if (! revenge)
-     return NULL;
-
-  line = _global->retaliation->Get_Random_Line();
-  pText = (char *) calloc (strlen (revenge->getName()) + 32 + strlen(line), sizeof (char));
-  if (! pText)
-    return NULL;
-
-  strcpy(pText, line);
-  strcat(pText, revenge->getName());
-  strcat(pText, " !!!");
-
-  return(pText);
-}
-
-int PLAYER::traceShellTrajectory (double aStartX, double aStartY, double aVelocityX, double aVelocityY, double &aReachedX, double &aReachedY)
-{
-  TANK *tankPool[10];						// For repulsion
-  bool bHasHit				= false;	// will be set to true if something is hit
-  bool bIsWallHit			= false;	// For steel walls and wrap floor/ceiling
-  bool bIsWrapped			=	false;	// For special handling in distance calcualtion when a shot goes through the wall
-  int iWallBounce 		= 0; 			// For wrap, rubber and spring walls
-  int iMaxBounce			=	pow((int)type, 2); // Useless can calculate 1, deadly bots 25 bounces
-  int iTargetDistance	=	ABSDISTANCE(_targetX, _targetY, aStartX, aStartY);
-  int iDistance				=	MAX_OVERSHOOT;
-  int iDirection			=	0;	// records the current direction of the shot
-  int iPriStageTicks	=	0;	// There is no unlimited tracking!
-  int iMaxPriStTicks	=	MAX_POWER * focusRate * 2.5;
-  int iSecStageTicks	=	0;	// rollers and burrowers can't be followed forever!
-  int iMaxSecStTicks	=	MAX_POWER * focusRate * 1.5;
-  double dDrag				=	weapon[tank->cw].drag;
-  double dMass				=	weapon[tank->cw].mass;
-  double dPosX				=	aStartX;
-  double dPosY				=	aStartY;
-  double dVelX				=	aVelocityX;
-  double dVelY				=	aVelocityY;
-  double dMaxVelocity = 0;
-  double dPrevX, dPrevY;	// to check pixels between two points
-  bool bIsSecondStage	=	false;	// Applies to burrowers and rollers
-
-  dMaxVelocity = _global->dMaxVelocity * (1.20 + (dMass / ((double)MAX_POWER / 10.0)));
-
-  // Set drag do the correct value:
-  if 			(ni[ITEM_DIMPLEP])
-    {
-      dDrag *= item[ITEM_DIMPLEP].vals[0];
-    }
-  else if (ni[ITEM_SLICKP])
-    {
-      dDrag *= item[ITEM_SLICKP].vals[0];
-    }
-
-  // Fill tankPool
-  for (int i = 0; i < _global->numPlayers; i++)
-    {
-      if ( (_global->players[i]) && (_global->players[i]->tank) )
-        tankPool[i] = _global->players[i]->tank;
-      else
-        tankPool[i] = NULL;
-    }
-
-  // Initial direction:
-  if (dVelX > 0.0) iDirection = 1;
-  if (dVelX < 0.0) iDirection = -1;
-
-  // On y va!
-  while (!bHasHit && !bIsWallHit && (iWallBounce < iMaxBounce)
-         && (iPriStageTicks < iMaxPriStTicks) && (iSecStageTicks < iMaxSecStTicks))
-    {
-      /*	---	First Stage - Applies to all weapons	---	*/
-      if (!bIsSecondStage)
-        {
-          // Apply Repulsor effects
-          for (int i = 0; i < _global->numPlayers; i++)
-            {
-              if (tankPool[i] && (tankPool[i] != tank))
-                {
-                  double xAccel = 0.0, yAccel = 0.0;
-                  tankPool[i]->repulse (dPosX + dVelX, dPosY + dVelY, &xAccel, &yAccel, tank->cw);
-                  if (tankPool[i] == _target)
-                    {
-                      // Without this, the shield would be nearly useless!
-                      xAccel *= focusRate;
-                      yAccel *= focusRate;
-                      // But the lesser bots wouldn't hit anything anymore if more than _target would be handled like that!
-                    }
-                  dVelX += xAccel;
-                  dVelY += yAccel;
-                }
-            }
-
-          dPrevX = dPosX;
-          dPrevY = dPosY;
-          //motion - wind affected
-          if (((dPosX + dVelX) < 1) || ((dPosX + dVelX) > (_global->screenWidth - 2)))
-            {
-              if	(	(((dPosX + dVelX) < 1) && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, 1, dPosY))
-                   ||(	((dPosX + dVelX) > (_global->screenWidth - 2))
-                       && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, (_global->screenWidth - 2), dPosY))	)
-                {
-                  dPosX = dPrevX;
-                  dPosY = dPrevY;
-                  bHasHit = true;
-                }
-              else
-                {
-                  switch (_env->current_wallType)
-                    {
-                    case WALL_RUBBER:
-                      dVelX = -dVelX;	//bounce on the border
-                      iWallBounce++;
-                      break;
-                    case WALL_SPRING:
-                      dVelX = -dVelX * SPRING_CHANGE;
-                      iWallBounce++;
-                      break;
-                    case WALL_WRAP:
-                      if (dVelX < 0)
-                        dPosX = _global->screenWidth - 2; // -1 is the wall itself
-                      else
-                        dPosX = 1;
-                      iWallBounce++;
-                      bIsWrapped = true;
-                      break;
-                    default:
-                      dPosX += dVelX;
-                      if (dPosX < 1)
-                        dPosX = 1;
-                      if (dPosX > (_global->screenWidth - 2))
-                        dPosX= _global->screenWidth - 2;
-                      dVelX = 0; // already applied!
-                      bIsWallHit = true;
-                    }
-                }
-            }
-
-          // hit floor or boxed top
-          if (	(	 (dPosY + dVelY) >= (_global->screenHeight - 1))
-               ||(((dPosY + dVelY) <= MENUHEIGHT) && _global->bIsBoxed))
-            {
-              if	(	(	_global->bIsBoxed && ((dPosY + dVelY) <= MENUHEIGHT)
-                     && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, MENUHEIGHT + 1))
-                   ||(	((dPosY + dVelY) > (_global->screenHeight - 2))
-                       && checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, (_global->screenHeight - 2)))	)
-                {
-                  dPosX = dPrevX;
-                  dPosY = dPrevY;
-                  bHasHit = true;
-                }
-              else
-                {
-                  switch (_env->current_wallType)
-                    {
-                    case WALL_RUBBER:
-                      dVelY = -dVelY * 0.5;
-                      dVelX *= 0.95;
-                      iWallBounce++;
-                      break;
-                    case WALL_SPRING:
-                      dVelY = -dVelY * SPRING_CHANGE;
-                      dVelX *= 1.05;
-                      iWallBounce++;
-                      break;
-                    default:
-                      // steel or wrap floor (ceiling)
-                      dPosY += dVelY;
-                      if (dPosY >= (_global->screenHeight - 1))
-                        dPosY = _global->screenHeight - 2; // -1 would be the floor itself!
-                      else
-                        dPosY= MENUHEIGHT + 1; // +1 or it would be the wall itself
-                      dVelY = 0; // already applied!
-                      bIsWallHit = true;
-                    }
-                  if (!bIsWallHit &&((fabs(dVelX) + fabs(dVelY)) < 0.8))
-                    bHasHit = true;
-                }
-            }
-
-          // velocity check:
-          double dActVelocity = ABSDISTANCE(dVelX, dVelY, 0, 0); // a²+b²=c² ... says Pythagoras :)
-          if ((dActVelocity > dMaxVelocity) && !bHasHit && !bIsWallHit)
-            {
-              // Velocity is applied first and modified by errorMultiplier
-              dPosY += dVelY * errorMultiplier;
-              dPosX += dVelX * errorMultiplier;
-              dVelX = 0.0;
-              dVelY = 0.0;
-              if ((dPosY <= MENUHEIGHT) && _global->bIsBoxed)
-                dPosY = MENUHEIGHT + 1;
-              if (dPosY > (_global->screenHeight - 2))
-                dPosY = _global->screenHeight - 2;
-              if (dPosX < 1)
-                {
-                  if (_env->current_wallType == WALL_WRAP)
-                    dPosX += _global->screenWidth - 2;
-                  else
-                    dPosX = 1;
-                }
-              if (dPosX > (_global->screenWidth - 2))
-                {
-                  if (_env->current_wallType == WALL_WRAP)
-                    dPosX -= _global->screenWidth - 2;
-                  else
-                    dPosX = _global->screenWidth - 2;
-                }
-              bHasHit = true;
-            }
-
-          // Now check for hits
-          if (!bHasHit && !bIsWallHit)
-            {
-              // Save Positions:
-              dPrevX = dPosX;
-              dPrevY = dPosY;
-              // Apply movement:
-              dPosX += dVelX;
-              dPosY += dVelY;
-              dVelX += (double)(_env->wind - dVelX) / dMass * dDrag * _env->viscosity;
-              dVelY += _env->gravity * (100.0 / _global->frames_per_second);
-              // Barrier test:
-              if ( (dVelY <= -1.0) && (dPosY <= (_global->screenHeight * -25.0)))
-                dVelY *= -1.0;
-
-              // See, if we have hit something
-              //	---	Environment-Test -- flight?
-              if (checkPixelsBetweenTwoPoints (_global, _env, &dPrevX, &dPrevY, dPosX, dPosY))
-                {
-                  dPosX = dPrevX;
-                  dPosY = dPrevY;
-                  bHasHit = true;
-                }
-            }
-          // Now that all modifications are applied, record direction:
-          if (dVelX > 0.0) iDirection = 1;
-          if (dVelX < 0.0) iDirection = -1;
-        }
-
-      /*	---	Second Stage - Applies to burrowers and rollers	---	*/
-      if (bIsSecondStage)
-        {
-          iSecStageTicks++;
-          // The weapon is on the ground and rolling or penetrating the ground:
-          if ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
-            {
-              // check whether we have hit anything
-              if (	(dPosX < 1)
-                   ||(dPosX > (_global->screenWidth - 2))
-                   ||(dPosY > (_global->screenHeight - 2))
-                   ||(getpixel(_env->terrain, (int)dPosX, (int)dPosY) != PINK))
-                bHasHit = true;
-              else
-                {
-                  // roll roller
-                  float fSurfY = (float)_env->surface[(int)dPosX] - 1;
-                  if ((fSurfY > dPosY) && (dPosY < (_global->screenHeight - 5)))
-                    {
-                      if (fSurfY < (dPosY + 5))
-                        dPosY = fSurfY;
-                      else
-                        dPosY+=5;
-                    }
-                  else
-                    {
-                      if (dVelX > 0.0)
-                        dPosX++;
-                      else
-                        dPosX--;
-                      if	(dPosY >= MENUHEIGHT)
-                        {
-                          if (fSurfY > dPosY)
-                            dPosY++;
-                          else if (fSurfY >= (dPosY - 2))
-                            dPosY = fSurfY - 1;
-                        }
-                    }
-                  if (dPosY > (_global->screenHeight - 5) && !bHasHit)
-                    dPosY = (_global->screenHeight - 5);
-                }
-            }
-
-          if ((tank->cw >= BURROWER) && (tank->cw <= PENETRATOR))
-            {
-              // Apply Repulsor effects, but not fully, as it is a burrower!
-              for (int i = 0; i < _global->numPlayers; i++)
-                {
-                  if (tankPool[i] && (tankPool[i] != tank))
-                    {
-                      double xAccel = 0.0, yAccel = 0.0;
-                      tankPool[i]->repulse (dPosX + dVelX, dPosY + dVelY, &xAccel, &yAccel, tank->cw);
-                      if (tankPool[i] == _target)
-                        {
-                          // Without this, the shield would be nearly useless!
-                          xAccel *= focusRate;
-                          yAccel *= focusRate;
-                          // But the lesser bots wouldn't hit anything anymore if more than _target would be handled like that!
-                        }
-                      dVelX += xAccel * 0.1;
-                      dVelY += yAccel * 0.1;
-                    }
-                }
-              if (((dPosX + dVelX) < 1) || ((dPosX + dVelX) > (_global->screenWidth-1)))
-                {
-                  // if the wall is rubber, then bouce
-                  if ( _env->current_wallType == WALL_RUBBER )
-                    dVelX = -dVelX;	//bounce on the border
-                  // bounce with more force
-                  else if ( _env->current_wallType == WALL_SPRING )
-                    dVelX = -dVelX * SPRING_CHANGE;
-                  // wall is steel, detonate
-                  else if ( _env->current_wallType == WALL_STEEL )
-                    bHasHit = true;
-                  // wrap around to other side of the screen
-                  else if ( _env->current_wallType == WALL_WRAP )
-                    {
-                      if (dVelX < 1)
-                        dPosX = _global->screenWidth - 2;
-                      else
-                        dPosX = 1;
-                    }
-                }
-              if ((dPosY + dVelY) >= (_global->screenHeight - 1))
-                {
-                  dVelY = -dVelY * 0.5;
-                  dVelX *= 0.95;
-                }
-              else if ((dPosY + dVelY) < MENUHEIGHT)   //hit screen top
-                {
-                  dVelY = -dVelY *0.5;
-                  dVelX *= 0.95;
-                }
-              dPosY += dVelY;
-              dPosX += dVelX;
-              dVelY -= _env->gravity * 0.05 * (100.0 / _global->frames_per_second);
-              if (getpixel (_env->terrain, (int)dPosX, (int)dPosY) == PINK)
-                bHasHit = true;
-            }
-        }
-      else
-        iPriStageTicks++;
-
-#ifdef DEBUG_AIM_SHOW
-      if (_global->bASD)
-        {
-          // Plot trajectories for debugging purposes
-          circlefill (screen, (int)dPosX, (int)dPosY, 2, color);
-        }
-#endif
-
-      /*	---	Third Stage - Applies to burrowers and rollers	---	*/
-      if	(	(!bIsSecondStage && (bHasHit || bIsWallHit))
-           &&(	((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
-               ||((tank->cw >= BURROWER) && (tank->cw <= PENETRATOR))))
-        {
-          // Only Rollers and Penetrators can enter the second stage!
-          bIsSecondStage = true;
-          bHasHit = false;
-          bIsWallHit = false;
-          if ((tank->cw >= SML_ROLLER) && (tank->cw <= DTH_ROLLER))
-            {
-              dPosY -= 5;
-              if (dPosX < 1)
-                dPosX = 1;
-              if (dPosX > (_global->screenWidth - 2))
-                dPosX = (_global->screenWidth - 2);
-
-              if	(	(dPosY >= _env->surface[(int)dPosX])				// y is surface or below
-                   &&(dPosY <= _env->surface[(int)dPosX] + 2) )  // but not burried more than 2 px
-                dPosY = _env->surface[(int)dPosX] - 1;
-
-
-              dVelX = 0.0;
-              if (getpixel (_env->terrain, (int)dPosX + 1, (int)dPosY + 1) == PINK)
-                dVelX = 1.0;
-              if (getpixel (_env->terrain, (int)dPosX - 1, (int)dPosY + 1) == PINK)
-                dVelX = -1.0;
-              if (dVelX == 0.0)
-                // if both sides are free (should be impossible, but might be a 1-pixel-peak) the shooting direction decides
-                dVelX = (double)iDirection;
-              dVelY = 0.0;
-            }
-          if ((tank->cw == BURROWER) || (tank->cw == PENETRATOR))
-            {
-              dVelX *= 0.1;
-              dVelY *= 0.1;
-            }
-          if ((dPosY <= MENUHEIGHT) && !_global->bIsBoxed)
-            dPosY = MENUHEIGHT + 1;
-          if ((dPosY <= MENUHEIGHT) && _global->bIsBoxed)
-            bIsWallHit = true; // Sorry, no more ceiling-drop!
-        }
-
-      /*	---	Fourth Stage - Tank hit test	---	*/
-      if (!bIsWallHit)
-        {
-          for (int i = 0; i < _global->numPlayers; i++)
-            {
-              if (tankPool[i])
-                {
-                  if	(	 (dPosX > (tankPool[i]->x - TANKWIDTH))
-                        &&(dPosX < (tankPool[i]->x + TANKWIDTH))
-                        &&(dPosY > (tankPool[i]->y))
-                        &&(dPosY < (tankPool[i]->y + TANKHEIGHT))
-                        &&(tankPool[i]->l > 0))
-                    bHasHit = true;
-                }
-            }
-        }
-
-      // if something is hit, be sure the values are in range and movement stopped!
-      if (bHasHit || bIsWallHit)
-        {
-          dVelX = 0.0;
-          dVelY = 0.0;
-          if (dPosY <= MENUHEIGHT)
-            dPosY = MENUHEIGHT + 1;
-          if (dPosY > (_global->screenHeight - 2))
-            dPosY = _global->screenHeight - 2;
-          if (dPosX < 1)
-            dPosX = 1;
-          if (dPosX > (_global->screenWidth - 2))
-            dPosX = _global->screenWidth - 2;
-        }
-    }
-
-#ifdef DEBUG_AIM_SHOW
-  if (_global->bASD)
-    {
-      // Targetting circle for debugging purposes
-      circlefill (screen, (int)dPosX, (int)dPosY, 10, BLACK);
-      circlefill (screen, (int)_targetX, (int)_targetY, 20, color);
-      LINUX_REST;
-    }
-#endif
-#ifdef DEBUG_AIM
-  if (bIsWallHit)
-    cout << "WALL ";
-  if (bHasHit)
-    cout << "HIT ";
-  if (iWallBounce >= iMaxBounce)
-    cout << "BOUNCE (" << iMaxBounce << ") ";
-  if (iPriStageTicks >= iMaxPriStTicks)
-    cout << "TICKS1 (" << iPriStageTicks << ") ";
-  if (iSecStageTicks >= iMaxSecStTicks)
-    cout << "TICKS2 (" << iSecStageTicks << ") ";
-#endif // DEBUG_AIM
-
-  // Now see where we are and calculate the distance difference between (char *)"hit" and "has to be hit"
-  if (!bIsWallHit && (iWallBounce < iMaxBounce))
-    {
-      iDistance = ABSDISTANCE(_targetX, _targetY, dPosX, dPosY);
-
-#ifdef DEBUG_AIM
-      cout << "(" << iDistance << " <- " << (int)dPosX << " x " << (int)dPosY << ") ";
-#endif // DEBUG_AIM
-
-      // Special handling for wrapped shots:
-      if (bIsWrapped)
-        {
-          int iHalfX = _global->halfWidth;
-
-          // Only shots where dPosX and _targetX are on opposite sides are relevant
-          if	(	(((int)dPosX < iHalfX) && (_targetX >=iHalfX))
-               ||(((int)dPosX >=iHalfX) && (_targetX < iHalfX))	)
-            {
-              if (((int)dPosX < iHalfX) && (_targetX >=iHalfX))
-                iDistance = ABSDISTANCE(dPosX + iHalfX, dPosY, _targetX, _targetY);
-              else
-                iDistance = ABSDISTANCE(_targetX + iHalfX, _targetY, dPosX, dPosY);
-            }
-          else
-            bIsWrapped = false; // not relevant
-        }
-
-      bool bIsWrongDir = false;
-      if	(!bIsWrapped
-          &&(	((dPosX < aStartX) && (_targetX > aStartX))
-              ||((dPosX > aStartX) && (_targetX < aStartX))	)	)
-        bIsWrongDir = true; // wrong side!
-
-      if (!iDirection)
-        {
-          // If we have no direction (very unlikely!) we can only guess by comparing x-coordinates:
-          if	(	(iTargetDistance > ABSDISTANCE(aStartX, aStartY, dPosX, dPosY))
-               ||(bIsWrongDir && (_env->current_wallType != WALL_STEEL))	)
-            // Too short or wrong direction, negate iDistance!
-            iDistance *= -1;
-        }
-      else
-        {
-          // with the help of the direction, we can exactly see, whether the shot is too short or too far
-          if	(!bIsWrapped	// This doesn't apply for wrapped shots!
-              &&(	(	(iDirection < 0.0) 		// shoot to the left...
-                    &&(dPosX > _targetX))		// ...and the shot hits right of target
-                  ||(	(iDirection > 0.0) 		// shoot to the right...
-                      &&(dPosX < _targetX))	)// ...and the shot hits left of target
-             )	iDistance *= -1; // too short!
-        }
-
-      if (bIsWrongDir && (_env->current_wallType == WALL_STEEL))
-        iDistance = MAX_OVERSHOOT; // wrong side and target unreachable!
-    }
-  else
-    iDistance = MAX_OVERSHOOT;
-
-  // Give the X- and Y-position back:
-  aReachedX = dPosX;
-  aReachedY	=	dPosY;
-
-  return(iDistance);
-}
-
-int PLAYER::rangeFind (int &aAngle, int &aPower)
-{
-#ifdef DEBUG_AIM
-printf("This is range find function\n");
-#endif
-  int iOvershoot			=	MAX_OVERSHOOT;
-  int iActOvershoot		= MAX_OVERSHOOT;
-  int iSpreadOvershoot= MAX_OVERSHOOT;
-  int iBestOvershoot	=	MAX_OVERSHOOT + 1; // So there will be at least one recorded!
-  int iLastOvershoot	=	MAX_OVERSHOOT;
-  int iAngle					=	aAngle;
-  int iAngleMod				=	0;
-  double dAngleMul		=	0.0;
-  double dPowerMul		=	0.0;
-  int iCalcAngle			= aAngle;
-  int iLastAngle 			= aAngle;
-  int iBestAngle			= aAngle;
-  int iAngleBarrier		=	aAngle;	// This will be the flattest angle possible, thus normalized aAngle + savety
-  int iIdealAngle			=	135;	// 135 translates to 45°, but will be raised if the barrier is higher
-  bool bIsRaisingMode = false;
-  int iPower					= aPower;
-  int iPowerMod				=	0;
-  int iPowerModFixed	=	0;
-  int iLastPower			= aPower;
-  int iBestPower			=	aPower;
-  int iAttempts				=	0;
-  int iSelfHitMult		=	0;
-  int iSpread					=	weapon[tank->cw].spread;
-  int iSpreadCount		=	0;
-  int iSpreadOdd[]		= {0,-1 * SPREAD, SPREAD, -2 * SPREAD, 2 * SPREAD};
-  int iSpreadEven[]		=	{ int(-0.5 * SPREAD), int(0.5 * SPREAD), int(-1.5 * SPREAD), 1, int(5 * SPREAD)};
-  int iMaxSpread			=	(int)type; // more intelligent bots can calculate more shots bearing spreads!
-  double dStartX, dStartY, dVelocityX, dVelocityY;	// No initialization needed, they are calculated anyway.
-  double dHitX, dHitY;
-
-#ifdef DEBUG_AIM
-printf("Starting RangeFind\n");
-#endif
-
-  // We need to be sure that iSpread is at least 1:
-  if (iSpread < 1)	iSpread	=	1;
-
-  // iMaxSpread needs to be adapted:
-  switch ((int)type)
-    {
-    case USELESS_PLAYER:
-    case GUESSER_PLAYER:
-      if (iSpread % 2)	iMaxSpread	=	1;
-      else							iMaxSpread	=	2;
-      break;
-    case RANGEFINDER_PLAYER:
-      if (iSpread % 2)	iMaxSpread	=	3;
-      else							iMaxSpread	=	4;
-      break;
-    case TARGETTER_PLAYER:
-      if (iSpread % 2)	iMaxSpread	=	5;
-      else							iMaxSpread	=	4;
-      break;
-    default:
-      if (iSpread % 2)	iMaxSpread	=	9; // I know there is nothing like that...
-      else							iMaxSpread	=	8; // ... but maybe in the future?
-      break;
-    }
-  if (iSpread > iMaxSpread)	iSpread = iMaxSpread;	// Now iSpread is limited to iMaxSpread...
-  // ...adapted to a test like (iSpreadCount < iSpread) but not larger then weapon[x].spread
-
-  /* Outline:
-   * RangeFind tries to adapt the given angle and power to minimize overshoot.
-   * to do so we have some facts to keep in mind:
-   * - ideal angle is 45° in both directions, giving most distance.
-   * - This angle translates into 135° for a shoot to the left, and 225° to the right
-   * - aAngle is considered to be the flattest shot possible, as it has been manipulated
-   *   by calculateAttackValues(), meaning that lowering it will lead to an obstacle
-   *   hit.
-   * - as a safety measure, when lowering an angle, a savety range of (int)type above
-   *   aAngle is applied
-   *
-   * The calculation is done in four steps:
-   * 1.: Calculate the real angle, as it is, at least for spreads, different with every shot shell
-   * 2.: Calculate the overshoot for each shot (or the one if no spread is given)
-   * 3.: Record angle, power and overshoot if the overshoot is smaller than the best recorderd so far
-   * 4.: Alter angle, or power if the angle can't be manipulated anymore, and start over, if the best
-   *     overshoot is != 0;
-   *
-   * This fourth step is divided into the following parts:
-   * a) short shots (overshoot < 0) - raise power
-   *    i.: angle lowering mode     - adjust the angle towards 45° until it (or aAngle) is reached
-   *    ii.:power raising mode      - after the first positive overshoot is reached, the angle won't be changed any more
-   * b) long shots (overshoot > 0)  - lower power
-   *    i.: angle raising mode      - adjust the angle towards 89° until it is reached
-   *    ii.:power lowering mode     - once the angle can't be changed any more, only power is changed
-   * angle raising/lowering modes are entered, and not left until iLastAngle == iAngle
-   *
-   * Note: Before you think all those step 4 calculations are wrong, they aren't, I am using a math trick to reduce
-   *       the amount of calculations:
-   *       180 + (180 - Angle) flips an angle between left and right. So the angle will be normalized to the right,
-   *       (aka 90° - 180°) and all calculations done there. If it was flipped, it will be flipped back for
-   *       traceShellTrajectory. This saves alot of (char *)"if(angle > 180)" lines!
-   */
-
-  // Preperations:
-  if (aAngle > 180)	iAngleBarrier = 180 + (180 - iAngleBarrier); // flip
-
-  // if we have some (char *)"space", add a savety distance:
-  if (iAngleBarrier < 170)	iAngleBarrier += (int)type - 1;
-
-  // We need some savety distance for spreads:
-  if (weapon[tank->cw].spread > 1)
-    {
-      // normal spread size:
-      iAngleBarrier += SPREAD * ((int)weapon[tank->cw].spread / 2);
-      if (!(weapon[tank->cw].spread % 2))
-        // Even spreads have half of SPREAD more than neccessary, so adapt:
-        iAngleBarrier -= SPREAD / 2;
-      // but be sure the barrier isn't too large:
-      if (iAngleBarrier > 179) iAngleBarrier = 179;
-    }
-
-  // Adapt the ideal angle if we are facing an obstacel:
-  if (iAngleBarrier > 135)	iIdealAngle = iAngleBarrier;
-
-#ifdef DEBUG_AIM
-  printf("New Target Try using....\n");
-  if (tank->cw < WEAPONS)
-    printf("%s\n", weapon[tank->cw].name);
-  else
-    printf("%s\n", item[tank->cw - WEAPONS].name);
-#endif // DEBUG_AIM
-
-  // Okay, here we go:
-  #ifdef DEBUG_AIM
-  printf("Best: %d .. Attempts %d  ..  RFattempts %d\n", iBestOvershoot, iAttempts, rangeFindAttempts);
-  #endif
-  while (iBestOvershoot && (iAttempts < rangeFindAttempts))
-    {
-      iAttempts++;
-
-#ifdef DEBUG_AIM
-      printf("--> %d. rangeFind:\n", iAttempts);
-      printf( (char *)" Angle: %3d Power: %4d\n", (iAngle - 90), iPower);
-#endif // DEBUG_AIM
-
-      /* --- Step 1: Calculate angle for spreads: --- */
-      iSpreadCount	= 0;
-      iOvershoot		=	MAX_OVERSHOOT;
-      iSpreadOvershoot	=	0;
-      iSelfHitMult	=	0;
-      iLastOvershoot= iOvershoot;
-
-      while (iSpreadCount < iSpread)
-        {
-          #ifdef DEBUG_AIM
-          printf("Inside the wee while loop.\n");
-          #endif
-          iCalcAngle		= iAngle;
-
-          // Two cases: Even and odd spreads.
-          if (iSpread > 1)
-            {
-              // odd spreads:
-              if (weapon[tank->cw].spread % 2)
-                iCalcAngle += iSpreadOdd[iSpreadCount];
-              // even spreads:
-              else
-                iCalcAngle += iSpreadEven[iSpreadCount];
-            }
-          dVelocityX = _global->slope[iCalcAngle][0] * (double)iPower * (100.0 / _global->frames_per_second) / 100.0;
-          dVelocityY = _global->slope[iCalcAngle][1] * (double)iPower * (100.0 / _global->frames_per_second) / 100.0;
-
-          dStartX = tank->x + (_global->slope[iCalcAngle][0] * GUNLENGTH);
-          dStartY	= tank->y + (_global->slope[iCalcAngle][1] * GUNLENGTH);
-
-          /* --- Step 2: Calculate overshoots: --- */
-          #ifdef DEBUG_AIM
-          printf("Trace the shell\n");
-          #endif
-          iActOvershoot = traceShellTrajectory(dStartX, dStartY, dVelocityX, dVelocityY, dHitX, dHitY);
-          #ifdef DEBUG_AIM
-          printf("Back from shell tracing.\n");
-          #endif
-          if (abs(iActOvershoot) < _global->screenWidth)
-            iSelfHitMult += adjustOvershoot(iActOvershoot, dHitX, dHitY);
-          // Otherwise it's a wall hit and not relevant!
-
-          #ifdef DEBUG_AIM
-          printf("Passed hit multi\n");
-          #endif
-          // With this method only the best hit of spreads is counted.
-          if (abs(iActOvershoot) < abs(iOvershoot))
-            iOvershoot = iActOvershoot;
-          
-          #ifdef DEBUG_AIM
-          printf("Passed overshoot. Spread: %d\n", iSpread);
-          #endif
-          // iSpreadOvershoot calculates the average absolute Overshoot
-          if (iActOvershoot)
-            iSpreadOvershoot += (int)(fabs((double)iActOvershoot) / (double)iSpread);
-
-          iSpreadCount++;
-
-#ifdef DEBUG_AIM
-          if (iSpreadCount > 1)
-            {
-              if (iSpreadCount == 2)
-                printf( (char *)" (%3d ", iCalcAngle - 90);
-              else
-                printf( (char *)",%3d ", iCalcAngle - 90);
-              if (iSpread == iSpreadCount)
-                printf( (char *)"\b)");
-            }
-#endif // DEBUG_AIM
-        }
-
-     #ifdef DEBUG_AIM
-     printf("Moving right along\n");
-     #endif
-
-      if (iOvershoot < 0)
-        iSpreadOvershoot *= -1;
-
-      if (iSelfHitMult > 0)
-        {
-          // We hit ourselves, so the larger Overshoot will be multiplied and taken!
-          if (abs(iSpreadOvershoot) > abs(iOvershoot))
-            iOvershoot = iSpreadOvershoot;
-          if (abs(iOvershoot) < _global->screenWidth)
-            iOvershoot = _global->screenWidth;
-          if (abs(iOvershoot) < MAX_OVERSHOOT)
-            iOvershoot*= iSelfHitMult;
-        }
-      else
-        {
-          // everything okay, just take the smaller one!
-          if (abs(iSpreadOvershoot) < abs(iOvershoot))
-            iOvershoot = (int)(((double)iSpreadOvershoot + ((double)iOvershoot * focusRate)) / 2);
-          else
-            iOvershoot = (int)(((double)iOvershoot + ((double)iSpreadOvershoot * focusRate)) / 2);
-        }
-
-      /* --- Step 3.: Record angle, power and overshoot if the overshoot is smaller than the best recorderd so far: --- */
-      if (abs(iOvershoot) < abs(iBestOvershoot))
-        {
-          iBestOvershoot	= iOvershoot;
-          iBestAngle			=	iAngle;
-          iBestPower			=	iPower;
-        }
-
-#ifdef DEBUG_AIM
-      printf( (char *)" Overshoot: %6d (%4d x %4d) SH: %1d\n", iOvershoot, (int)dHitX, (int)dHitY, iSelfHitMult);
-#endif // DEBUG_AIM
-
-      /* --- Step 4.: Alter angle, or power if the angle can't be manipulated anymore, and start over: --- */
-      if (iOvershoot)
-        {
-          // Preperation: flip iAngle if neccessary
-          if (iAngle > 180) iAngle = 180 + (180 - iAngle); // flip
-
-          // Preperation: Decide over angle and power modification depending on overshoot
-          if (abs(iOvershoot) < _global->screenWidth)
-            {
-              dAngleMul = 1.0 + fabs((double)iOvershoot / (double)_global->screenWidth); // between 1.0 and 2.0
-              iAngleMod = (int)(fabs((double)iOvershoot) / 10.0);
-              if (iAngleMod > 15)
-                iAngleMod = 15; // need a barrier here, too
-
-              // Power modification is calculated depending on overshoot
-              // To raise or lower by 100 pixels, we need aproximately 100 power (at 45°)
-              dPowerMul				=	pow(1.0 + (fabs((double)iAngle - 135.0) / 50.0), 2.0); 		// between 1.0 and 3,61
-              iPowerModFixed	=	5 + (int)(				(double)type	*	(fabs(iOvershoot) / 10.0)); // useless 10%, deadly 50% fix
-              iPowerMod				=	5 + (int)((10.0	-	(double)type)	*	(fabs(iOvershoot) / 10.0)); // useless 90%, deadly 50% variable
-              iPowerMod = (rand() % iPowerMod) + iPowerModFixed;
-            }
-          else
-            {
-              // As the overshoot is too high, probably a wall hit, Modification is done in a more limited way:
-              dAngleMul = 1.0 + ((double)type / 10.0);
-              dPowerMul = 1.0 + ((double)type / 10.0);
-              iAngleMod = (rand() % 11) + 5;
-              iPowerMod	=	(MAX_POWER / 8) + (rand() % (MAX_POWER / 8));
-              if ((iOvershoot < 0) || (iAngle >= 170))
-                // we need to raise distance urgently, so cancel bIsRaisingMod
-                bIsRaisingMode = false;
-              else
-                // we need to lower distance urgently, so enter bIsRaisingMod
-                bIsRaisingMode = true;
-            }
-
-          // before entering the step 4 modification parts, we could try a trick:
-          if	(	(abs(iLastOvershoot) < abs(iOvershoot))
-               &&(	((iLastOvershoot < 0) && (iOvershoot > 0))		// be sure that the overshoots switches
-                   ||((iLastOvershoot > 0) && (iOvershoot < 0))	) // between signed and unsigned
-               &&(	(abs(iLastAngle - iAngle) >= 2)								// There has to be something to do or
-                   ||(abs(iLastPower - iPower) >=10)	)						// we might waste all remaining attempts
-               &&(abs(iLastOvershoot) < _global->screenWidth)			// don't try it on wallhits, selfhits
-               &&(abs(iOvershoot) < _global->screenWidth)	)
-            {
-              // the current modification made the shot worse,
-              // but switched between too short and too long,
-              // so revert to half the modification:
-              iAngleMod		= (iLastAngle + iAngle) / 2; // We use the mod to save declaring
-              iPowerMod		= (iLastPower + iPower) / 2; // two new vars!
-              iLastAngle	= iAngle;
-              iLastPower	= iPower;
-              iAngle			= iAngleMod;
-              iPower			= iPowerMod;
-              // Note: This trick won't work when both are too short or too long,
-              // because then bots would never get over too high obstacles!
-            }
-          else
-            {
-              // No trick needed, we are getting nearer!
-              iLastAngle	=	iAngle;
-              iLastPower	=	iPower;
-              iAngleMod		=	(int)((double)iAngleMod * focusRate * dAngleMul);
-              iPowerMod		=	(int)((double)iPowerMod * focusRate * dPowerMul);
-              //	 * a) short shots (overshoot < 0) - raise power
-              if (iOvershoot < 0)
-                {
-                  // If we are too short and have (char *)"overbend" in raising mode, it has to be cancelled!
-                  if (bIsRaisingMode && (iAngle > 180))
-                    bIsRaisingMode = false;
-
-                  if (!bIsRaisingMode)
-                    {
-                      //	 *    i.: angle lowering mode     - adjust the angle towards 45° until it (or aAngle) is reached
-                      if (iAngle > iIdealAngle)
-                        {
-                          iAngle -= iAngleMod;
-                          if (iAngle < iIdealAngle)	iAngle = iIdealAngle;
-                        }
-                      if (iAngle < iIdealAngle)
-                        {
-                          iAngle += iAngleMod;
-                          if (iAngle > iIdealAngle) iAngle = iIdealAngle;
-                        }
-
-                      // Apply as much power as is neccessary:
-                      iPower += iPowerMod - (abs(iLastAngle - iAngle) * 10);
-
-                      // check to see whether Raising mode should be entered:
-                      if ((iAngle == iIdealAngle) && (iAngle == iLastAngle))
-                        bIsRaisingMode = true;
-                    }
-                  else
-                    //	 *    ii.:power raising mode      - after the first positive overshoot is reached, the angle won't be changed any more
-                    iPower += (int)((double)iPowerMod * dAngleMul);
-                }
-              //	 * b) long shots (overshoot > 0)  - lower power
-              if (iOvershoot > 0)
-                {
-                  if (bIsRaisingMode)
-                    {
-                      //   *    i.: angle raising mode      - adjust the angle towards 89° until it is reached
-                      if (iAngle > 178)
-                        iAngleMod = 1;  // for small (char *)"overbends"
-                      iAngle += iAngleMod;
-
-                      // Apply as much power as is neccessary:
-                      iPower -= iPowerMod - (abs(iLastAngle - iAngle) * 10);
-                    }
-                  else
-                    //   *    ii.:power lowering mode     - once the angle can't be changed any more, only power is changed
-                    iPower -= (int)((double)iPowerMod * dAngleMul);
-                }
-            }
-
-          // last step: check iPower and iAngle:
-          while	(	(iPower >= MAX_POWER)
-                  ||(iPower <= (MAX_POWER / 20)))
-            iPower = ((MAX_POWER / 2) + iPower) / 2;
-          iPower -= iPower % 5;
-
-          // check the angle, it must not be flatter than iAngleBarrirer!
-          if (iAngle < iAngleBarrier)
-            iAngle = iAngleBarrier;
-
-          // Now flip the angle back if neccessary:
-          if (aAngle > 180)
-            iAngle = 180 + (180 - iAngle); // flip back!
-
-#ifdef DEBUG_AIM
-          printf( (char *)" --> AngleMod: %3d PowerMod: %4d\n", (iAngle - iLastAngle), (iPower - iLastPower));
-#endif // DEBUG_AIM
-        } // end of if(iOvershoot)
-    }
-
-#ifdef DEBUG_AIM
-  printf("looks like the end of the while loop in aiming\n");
-#endif
-  // Record the best found values in tank if possible
-  if (abs(iBestOvershoot) < tank->smallestOvershoot)
-    {
-#ifdef DEBUG_AIM
-      printf( (char *)" New best Overshoot: %5d\n", iBestOvershoot);
-#endif // DEBUG_AIM
-      tank->smallestOvershoot = abs(iBestOvershoot);
-      tank->bestAngle					=	iBestAngle;
-      tank->bestPower					=	iBestPower;
-      // Give the best ones back if possible
-      if (iBestAngle != aAngle)	aAngle = iBestAngle;
-      if (iBestPower != aPower)	aPower = iBestPower;
-    }
-#ifdef DEBUG_AIM
-  else
-    cout << " No new best Overshoot..." << endl;
-#endif // DEBUG_AIM
-  return(iBestOvershoot);
-}
-
-int PLAYER::adjustOvershoot(int &aOvershoot, double aReachedX, double aReachedY)
-{
-  TANK *pTankHit			=	NULL;				//	For hitting quality analysis
-  long int iOvershoot	=	aOvershoot;	// To calculate with locally
-  bool bIsDirectHit		=	true;				//	false for shaped charges and napalm is chosen
-  bool bIsShaped			=	false;			//	true for shaped charges (special radius calculation needed!)
-  int iDamage					=	weapon[tank->cw].damage;
-  int iRadius					=	weapon[tank->cw].radius;
-  int iSelfHits				=	0;					// Will be raised for every self- and team-hit and then returned
-
-  if (iRadius < 10)	iRadius = 10;	// several things wouldn't function otherwise
-  if (iDamage < 10)	iDamage = 10; // if we didn't set minimum values
-
-  if ((tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
-    bIsShaped = true;
-  if (bIsShaped || ((tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM)))
-    bIsDirectHit = false;
-
-  /* --- Step 1: See whether a tank is hit: */
-  for (int i = 0; i < 10; i++)
-    {
-      if (_global->players[i] && _global->players[i]->tank)
-        {
-          if	(	(aReachedX > (_global->players[i]->tank->x - TANKWIDTH))
-               &&(aReachedX < (_global->players[i]->tank->x + TANKWIDTH))
-               &&(aReachedY > (_global->players[i]->tank->y))
-               &&(aReachedY < (_global->players[i]->tank->y + TANKHEIGHT))
-               &&(_global->players[i]->tank->l > 0))
-            pTankHit = _global->players[i]->tank;
-        }
-    }
-
-  /* --- Step 2: See whether the target tank is hit or in weapon radius: --- */
-  // check these values in case of segfault
-  if ( (_target) && (pTankHit) &&
-       (pTankHit == _target) && bIsDirectHit && (pTankHit->player != this))
-    // A Direct hit with a direct weapon is what we want, so give 0 back
-    {
-      iOvershoot = 0;
-#ifdef DEBUG_AIM
-      cout << "ON TARGET! ";
-#endif // DEBUG_AIM
-    }
-  else
-    {
-      if (!iOvershoot)
-        {
-          if	(	(	((tank->x < _targetX) && (_targetX > aReachedX))
-                 ||((tank->x > _targetX) && (_targetX < aReachedX)) )
-               &&(iOvershoot > 0)
-               &&(iOvershoot < MAX_OVERSHOOT)	)
-            iOvershoot *= -1; // the shot is too short
-          else
-            iOvershoot = 1;
-        }
-      if (pTankHit)
-        {
-          // We *have* hit a tank, let's see to that it isn't us or a friend:
-          if ((pTankHit == tank) || (pTankHit->player == this))
-            {
-              // Ourselves, not good!
-              iOvershoot *= iRadius * iDamage;
-              iSelfHits++;
-            }
-          else if (pTankHit->player->team == team)
-            {
-              // We hit someone of the same team, but only Jedi and SitH care, of course:
-              if (team == TEAM_JEDI)
-                iOvershoot *= iRadius * (defensive + 2) * focusRate;
-              if (team == TEAM_SITH)
-                iOvershoot *= iRadius * (-1 * (defensive - 2)) * focusRate;
-              if (team != TEAM_NEUTRAL)
-                iSelfHits++;
-            }
-        }
-
-      /* --- Step 3: See, whether we, or a team member, is in blast radius and manipulate Overshoot accordingly --- */
-      for (int i = 0; i < 10; i++)
-        {
-          if (_global->players[i] && _global->players[i]->tank && (_global->players[i]->tank != _target))
-            {
-              // _target is skipped, so we don't get wrong values when revenging on a team mate!
-              int iX					= _global->players[i]->tank->x;
-              int iY 					= _global->players[i]->tank->y;
-              int iRadiusDist;
-              int iBlastDist	= ABSDISTANCE(aReachedX, aReachedY, iX, iY);
-
-              iRadiusDist = iRadius - iBlastDist;
-              if (!iRadiusDist)	iRadiusDist = 1;
-
-              if (iBlastDist < iRadius)
-                {
-                  // Is in Blast range. (maybe)
-                  if (	!bIsShaped
-                       ||(bIsShaped && (abs(iY - (int)aReachedY) <= (iRadius / 20))))
-                    {
-                      // Either no shaped charge or in radius
-                      if (_global->players[i]->tank == tank)
-                        {
-                          // Ourselves, not good!
-                          iOvershoot *= iRadiusDist * iDamage;
-                          iSelfHits++;
-                        }
-                      else if (_global->players[i]->team == team)
-                        {
-                          // We hit someone of the same team, but only Jedi and SitH care, of course:
-                          if (team == TEAM_JEDI)
-                            iOvershoot *= iRadiusDist * (defensive + 2) * focusRate;
-                          if (team == TEAM_SITH)
-                            iOvershoot *= iRadiusDist * (-1 * (defensive - 2)) * focusRate;
-                          if (team != TEAM_NEUTRAL)
-                            iSelfHits++;
-                        }
-                    }
-                }
-            }
-        }
-
-      // Be sure to not give a ridiculously high overshoot back:
-      while (abs(iOvershoot) >= MAX_OVERSHOOT)
-        iOvershoot /= 2;
-    }
-  aOvershoot = (int)iOvershoot;
-
-  return(iSelfHits);
-}
-
-// If Napalm or a shaped weapon is chosen, the target has to be modified!
-int PLAYER::getAdjustedTargetX(TANK * aTarget)
-{
-  int iTargetX, iTargetY;
-  int iMinOffset	=	0;
-  int iMaxOffset	= 1;
-
-  if (aTarget)
-    {
-      iTargetX  = aTarget->x;
-      iTargetY  = aTarget->y;
-    }
-  else if (_target)
-    {
-      iTargetX  = _target->x;
-      iTargetY  = _target->y;
-    }
-  else   // avoid segfault
-    return ( rand() % _global->screenWidth ) ;
-
-
-  // tank is dead, bail out to avoid segfault
-  if (! tank)
-      return iTargetX;
-
-  if ( (tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
-    {
-      int iBestLeftOffset	=	0;
-      int iBestRightOffset	=	0;
-      int iLeftY = 0;
-      int iBestLeftY = 0;
-      int iRightY = 0;
-      int iBestRightY = 0;
-      iMinOffset	=	(int)((TANKWIDTH / 2) + ((double)TANKWIDTH * focusRate));
-      iMaxOffset	= iMinOffset + (TANKWIDTH * (((int)type + 1) / 2));
-
-      for (int i = iMinOffset; i < iMaxOffset; i++)
-        {
-          // Get Y-Data:
-          if ((iTargetX - i) > 1)
-            iLeftY = _env->surface[iTargetX - i];
-          if ((iTargetX + i) < (_global->screenWidth - 1))
-            iRightY= _env->surface[iTargetX + i];
-          // Check whether new Y-Data is better than what we have:
-          if (abs(iLeftY - iTargetY) < abs(iBestLeftY - iTargetY))
-            {
-              iBestLeftOffset = i;
-              iBestLeftY 			= iLeftY;
-            }
-          if (abs(iRightY - iTargetY) < abs(iBestRightY - iTargetY))
-            {
-              iBestRightOffset	= i;
-              iBestRightY				=	iRightY;
-            }
-        }
-      // Now see whether we go left or right:
-      if (abs(iBestLeftY - iTargetY) < abs(iBestRightY - iTargetY))
-        // use left:
-        iTargetX -= iBestLeftOffset;
-      else
-        // use right:
-        iTargetX += iBestRightOffset;
-    }
-
-  if ( (tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM))
-    {
-      // here we only check one side, the one to the wind:
-      iMaxOffset			= abs((int)((double)iMaxOffset * _env->wind * focusRate));
-      iMinOffset			=	abs((int)(_env->wind * (double)TANKWIDTH * focusRate));
-      int iSurfY			=	0;
-      int iBestY			=	0;
-      int iOffset			=	0;
-      int iBestOffset	=	0;
-      int iDirection	=	0;
-
-      if (_env->wind < 0)	iDirection = 1;		// for some reason I do not know, wind is
-      if (_env->wind > 0)	iDirection = -1;	// used (char *)"the wrong way". (???)
-
-      // Don't stretch the offset onto ourselves:
-      if	(	((tank->x < iTargetX) && ((iTargetX - tank->x - (iDirection * iMaxOffset)) < (TANKWIDTH * 2)))
-           ||((tank->x > iTargetX) && ((tank->x - iTargetX - (iDirection * iMaxOffset)) < (TANKWIDTH * 2)))	)
-        iMaxOffset = abs(iTargetX - (int)tank->x) - (TANKWIDTH * 2);
-
-      // And don't allow a negative offset either (would be useless due to wind!)
-      if (iMaxOffset < TANKWIDTH)
-        iMaxOffset = TANKWIDTH;
-
-      // But be sure iMinOffset is smaller than iMaxOffset:
-      if (iMinOffset >= iMaxOffset)
-        iMinOffset = iMaxOffset - (int)type;
-
-      if (iDirection)
-        {
-          // Without wind there is nothing to do!
-          for (int i = iMinOffset; i < iMaxOffset; i++)
-            {
-              iOffset = i * iDirection;
-              // Get Y-Data:
-              if (((iTargetX + iOffset) > 1) && (((iTargetX + iOffset) < (_global->screenWidth - 1))))
-                iSurfY = _env->surface[iTargetX + iOffset];
-
-              // Check whether new Y-Data is better than what we have:
-              if	(	((iTargetY - iSurfY) < (iTargetY - iBestY))
-                   ||(!iBestY)	)
-                {
-                  iBestOffset = iOffset;
-                  iBestY 			= iSurfY;
-                }
-            }
-          iTargetX += iBestOffset;
-        }
-    }
-
-  return (iTargetX);
-}
-
-int PLAYER::calculateAttack(TANK *aTarget)
-{
-  /* There are two general possibilities:
-   * - aTarget provided:
-   *   This function was called from computerSelectTarget() and will only check if it is possible
-   *   to reach a target, aka try only once and give the overshoot back.
-   * - default (aTarget is automatically NULL)
-   *   This function was called from computerControl() and will go forth and try to hit _target
-   *
-   * Outline:
-   * --------
-   * There are the following possibilities:
-   * a) An Item or a laser is chosen
-   *    -> a direct angle will do!
-   * b) We are burried
-   *    -> fire unburying tool at +/-60° to the middle of the screen
-   * c) Kamikaze
-   *    -> indicated by targetX/Y being tank.x/y
-   *    -> if shaped weapon is chosen, fire 45° and power 100 to the side where y is nearest to tank.y
-   *    -> if napalm is chosen, fire 90° and power 0
-   *    -> otherwise fire 90° and power 250
-   * d) Fire in non-boxed mode
-   *    -> normal calculation
-   * e) Fire in non-boxed mode
-   *    -> extended power-control after normal calculation
-   *    -> if the target can't be reached while staying below the ceiling,
-   *       check for an obstacle than can be removed and do so if found.
-   * --> boxed mode is included in non-boxed mode now. I hope it works as I intent!
-   *
-   * Update for dynamization: The previous target is recorded, aiming starts at the old values,
-   *                          if the target didn't change. */
-
-  int iXdistance, iYdistance;
-  if (aTarget)
-    {
-      iXdistance = aTarget->x - tank->x;
-      iYdistance = aTarget->y - tank->y;
-    }
-  else
-    {
-      iXdistance = _targetX - tank->x;
-      iYdistance = _targetY - tank->y;
-    }
-
-#ifdef DEBUG_AIM
-  if (!aTarget && !iTargettingRound)
-    {
-      printf("\n-----------------------------------------------\n");
-      if (_target)
-         printf("%s is starting to aim at %s\n", getName(), _target->player->getName() );
-      else
-         printf("%s is aiming without a target!\n", getName());
-    }
-#endif // DEBUG_AIM
-
-  /* --- case a) An Item is chosen --- */
-  if	( (!aTarget) && ( (tank->cw >= WEAPONS) ||
-          ((tank->cw >= SML_LAZER) && (tank->cw <= LRG_LAZER))))
-    {
-#ifdef DEBUG_AIM
-      printf("About to calc direct angle.\n");
-#endif
-      _targetAngle = calculateDirectAngle (iXdistance, iYdistance);
-      _targetPower = MAX_POWER / 2;
-
-#ifdef DEBUG_AIM
-      cout << "Direct " << _targetAngle - 90 << " for ";
-      printf("Direct %d for %s\n", _targetAngle -90, (tank->cw < WEAPONS) ? weapon[tank->cw].name : item[tank->cw - WEAPONS].name);
-#endif // DEBUG_AIM
-
-      iTargettingRound = retargetAttempts; // So it is done!
-
-      return(0);
-    }
-
-
-  /* --- case b) We are burried --- */
-  if (!aTarget &&(tank->howBuried () > BURIED_LEVEL))
-    {
-      // Angle is 60° to the middle of the screen:
-      int iAngleVariation = rand() % (20 - ((int)type * 3));
-      iAngleVariation -= (int)((double)type / 1.5);
-      if (tank->x <= _global->halfWidth)
-        _targetAngle = 150 + iAngleVariation;
-      else
-        _targetAngle = 210 - iAngleVariation;
-#ifdef DEBUG_AIM
-      printf("Freeing self with Angle %d and Power %d\n", _targetAngle, _targetPower);
-#endif // DEBUG_AIM
-
-      iTargettingRound = retargetAttempts;
-
-      return(0);
-    }
-
-  /* --- case c) Kamikaze --- */
-  if (!aTarget && (_targetX == tank->x) && (_targetY == tank->y))
-    {
-#ifdef DEBUG_AIM
-      cout << "Going bye bye with ";
-      if (tank->cw < WEAPONS)
-        cout << weapon[tank->cw].name;
-      else
-        cout << item[tank->cw - WEAPONS].name;
-      cout << " !!!" << endl;
-      cout << "GERONNNNNIIIIIMOOOOOOOOOO !!!" << endl;
-#endif // DEBUG_AIM
-
-      iTargettingRound = retargetAttempts;
-
-      // For a nice bye bye we set angle/power directly
-      if ((tank->cw >= SHAPED_CHARGE) && (tank->cw <= CUTTER))
-        {
-          // shaped weapons are a bit special:
-          int iHLeft, iHRight;
-          int iCount = 0;
-          for (int i = TANKWIDTH; i <= (TANKWIDTH * 2); i++)
-            {
-              iCount++;
-              iHLeft = _env->surface[(int)(tank->x - i)];
-              iHRight= _env->surface[(int)(tank->x + i)];
-            }
-          iHRight /= iCount;
-          iHLeft  /= iCount;
-          if (fabs(iHRight - tank->y) < fabs(iHLeft - tank->y))
-            _targetAngle = 135;
-          else
-            _targetAngle = 225;
-          _targetPower = MAX_POWER / 20;
-          return(0);
-        }
-      // The other possibilities are easier:
-      if ((tank->cw >= SML_NAPALM) && (tank->cw <= LRG_NAPALM))
-        _targetPower = 0;
-      else
-        _targetPower = MAX_POWER / 8;
-      _targetAngle = 180;
-
-      return(0);
-    }
-
-  /* --- case d) Fire in non-boxed mode --- */
-  int iRawAngle, iAdjAngle, iPower, iSavetyPower;
-  int iOvershoot = MAX_OVERSHOOT;
-  int iLastOvershoot = MAX_OVERSHOOT;
-  int iBestOvershoot = MAX_OVERSHOOT;
-
-  _targetX = getAdjustedTargetX(aTarget);
-  calculateAttackValues(iXdistance, iYdistance, iRawAngle, iAdjAngle, iPower, false);
-
-  if (!iTargettingRound)
-    {
-      // Initializiation, if this is the first try:
-
-      if (!aTarget && (_oldTarget == _target) )
-        {
-          // Target didn't change, use last rounds values:
-          tank->bestAngle = tank->a;
-          tank->bestPower = tank->p;
-          iRawAngle = tank->a;
-          iAdjAngle = tank->a;
-          iPower    = tank->p;
-        }
-      else
-        {
-          // new target, new values:
-          tank->bestAngle = 180;
-          tank->bestPower = MAX_POWER;
-          if (!aTarget)
-            _oldTarget = _target;
-        }
-
-      tank->smallestOvershoot = MAX_OVERSHOOT + 1; // So there will be at least one recorded
-
-    }
-  else
-    {
-      // This is a follow-up round, get the last values back:
-      iAdjAngle = _targetAngle;
-      iPower    = _targetPower;
-      iBestOvershoot = tank->smallestOvershoot;
-    }
-
-  if (aTarget)
-  {
-    #ifdef DEBUG_AIM
-    printf("Returning a range find.\n");
-    #endif
-    return(rangeFind(iAdjAngle, iPower));
-  }
-
-  // Now that we are here, normal handling is needed:
-#ifdef DEBUG_AIM
-  if (!iTargettingRound)
-    {
-      printf("Intitial try:\n");
-      printf("About to range find.\n");
-    }
-#endif // DEBUG_AIM
-
-  iOvershoot			= rangeFind(iAdjAngle, iPower);
-  iLastOvershoot	=	iBestOvershoot;
-  if (abs(iOvershoot) < abs(iBestOvershoot))
-    iBestOvershoot = iOvershoot;
-
-#ifdef DEBUG_AIM
-  cout << "Overshoot: " << iOvershoot << " (best so far: " << iBestOvershoot << ")" << endl << endl;
-  cout << iTargettingRound + 1 << ". re-try:";
-#endif // DEBUG_AIM
-
-  // There is still an Overshoot, so see what we can do:
-  if (abs(iLastOvershoot - iOvershoot) <= 1)
-    {
-      // rangeFind can't get a better version, so start completely different!
-      int iAngleRange = 180;
-      if (_env->current_wallType == WALL_STEEL)
-        iAngleRange /= 2;
-      if (_targetX > tank->x)
-        // shoot basically to the right:
-        iRawAngle = 90 + (rand() % iAngleRange);
-      else
-        // shoot basically to the left:
-        iRawAngle = 270 - (rand() % iAngleRange);
-      iAdjAngle = (iAdjAngle + iRawAngle) / 2;
-      iPower		=	(iPower + (rand() % (MAX_POWER / 2))) / 2;
-#ifdef DEBUG_AIM
-      cout << " Starting over! " << endl;
-#endif
-    }
-  else
-    {
-      // Just calculate anew:
-      calculateAttackValues(iXdistance, iYdistance, iRawAngle, iAdjAngle, iPower, true);
-#ifdef DEBUG_AIM
-      cout << " recalculating! " << endl;
-#endif
-    }
-
-  /* SavetyPower is as follows:
-   * abs(iAdjAngle -180) changes the value to be between:
-   * ->  0 : Straight upwards       (originally 180)
-   * -> 90 : Straight Left or right (originally 270/90)
-   * 91 - abs(...) gives a value between 1 and 91 which is multiplied with the radius */
-  iSavetyPower = ((91 - abs(iAdjAngle - 180)) * (weapon[tank->cw].radius * focusRate)) / focusRate;
-  if (iSavetyPower > (MAX_POWER / 4))	iSavetyPower	= MAX_POWER / 4;
-  if (iPower < iSavetyPower)					iPower 				= iSavetyPower;
-
-  iLastOvershoot = iOvershoot;
-  iOvershoot = rangeFind(iAdjAngle, iPower);
-
-  if (abs(iOvershoot) < abs(iBestOvershoot))
-    iBestOvershoot = iOvershoot;
-
-  iTargettingRound++;
-
-  // Do not count this attempt if the currently best overshoot is too high:
-  if ((iBestOvershoot > _global->screenWidth) && (rand() % ((int)type * 2)))
-    iTargettingRound--; // if ibestOvershoot is negative it will alwys be counted!
-
-  if (tank->smallestOvershoot < (weapon[tank->cw].radius / (int)type))
-    iTargettingRound = retargetAttempts;
-
-  // Now the best Version has to be tested:
-  iOvershoot	= tank->smallestOvershoot; // Always a positive number!
-  _targetAngle = tank->bestAngle; // Record for foolow-up rounds
-  _targetPower = tank->bestPower; // Record for follow-up rounds
-
-  if (iTargettingRound == retargetAttempts)
-    {
-#ifdef DEBUG_AIM
-      cout << "  ---  ---" << endl;
-      cout << "Final Decision:" << endl;
-      cout << "Angle    : " << _targetAngle - 90 << endl;
-      cout << "Power    : " << _targetPower << endl;
-      cout << "Overshoot: " << iBestOvershoot << endl;
-#endif // DEBUG_AIM
-
-      if (iOvershoot > weapon[tank->cw].radius)
-        {
-          if (_targetAngle > 180)
-            iRawAngle = _targetAngle + 10 + (rand() % ((int)type * 4));
-          else
-            iRawAngle = _targetAngle - (10 + (rand() % ((int)type * 4)));
-
-          // There are three possibilities:
-          if	(	(iBestOvershoot < 0)
-               &&(abs(_targetAngle - 180) <= (10 + ((iXdistance / _global->screenWidth) * 10)))
-               &&(!tank->shootClearance(iRawAngle, weapon[tank->cw].radius * 1.5)))
-            {
-              // a) there is an obstacle in our way!
-              tank->cw = getUnburyingTool();
-              if (tank->cw < WEAPONS)
-                {
-                  _targetAngle = iRawAngle;
-#ifdef DEBUG_AIM
-                  cout << "Revised Angle for unburying: " << _targetAngle - 90 << endl;
-#endif // DEBUG_AIM
-                }
-            }
-          else
-            {
-              // b) the way is clear, so either try to switch weapon or fire away!
-              if (iBestOvershoot < 0)
-                {
-                  // As we are too short, see whether another weapon might be good
-                  if (nm[TREMOR])	tank->cw = TREMOR;
-                  if (nm[SHOCKWAVE])	tank->cw = SHOCKWAVE;
-                  if ((_targetY < (_global->screenHeight - iOvershoot)) && nm[BURROWER])
-                    tank->cw = BURROWER;
-                  if (nm[TECTONIC])	tank->cw = TECTONIC;
-                  if ((_targetY < (_global->screenHeight - iOvershoot)) && nm[PENETRATOR])
-                    tank->cw = PENETRATOR;
-
-                  // if it is boxed, it is better to teleport out!
-                  if ((_global->bIsBoxed) && (iOvershoot > (weapon[tank->cw].radius * 2)))
-                    {
-                      // no way, teleport out!
-                      if (ni[ITEM_TELEPORT])	tank->cw = ITEM_TELEPORT + WEAPONS;
-                      if (ni[ITEM_SWAPPER])		tank->cw = ITEM_SWAPPER + WEAPONS;
-                    }
-                }
-              else
-                {
-                  // look for tectonis and their range
-                  if (nm[TREMOR]		&& (weapon[TREMOR].radius >= iOvershoot))			tank->cw = TREMOR;
-                  if (nm[SHOCKWAVE] && (weapon[SHOCKWAVE].radius >= iOvershoot))	tank->cw = SHOCKWAVE;
-                  if (nm[TECTONIC]	&& (weapon[TECTONIC].radius >= iOvershoot))		tank->cw = TECTONIC;
-                }
-
-              // c) we have no chance to reach anything!
-              if (iOvershoot > _global->screenWidth)
-                {
-                  // no way, teleport out!
-                  if (ni[ITEM_TELEPORT])	tank->cw = ITEM_TELEPORT + WEAPONS;
-                  if (ni[ITEM_SWAPPER])		tank->cw = ITEM_SWAPPER + WEAPONS;
-                }
-
-            }
-        }
-      else
-        {
-          // If we *are* hitting ok, Angle and Power need to be manipulated by errorMultiplier
-          int iAngleMod, iPowerMod;
-          int iAngleModLimit = (42 - (10 * ((int)type - 1))); 	// =  42,  32,  22, 12,  2 for useless -> deadly
-          int iPowerModLimit = (210 - (50 * ((int)type - 1)));	// = 210, 160, 110, 60, 10 for useless -> deadly
-
-          iAngleMod = (rand() % 51) * errorMultiplier;	//	useless: 0 -  100, deadly: 0 - 2 (with 2 being very unlikely)
-          if (iAngleMod > iAngleModLimit)
-            iAngleMod = iAngleModLimit;
-
-          iPowerMod	=	(rand() % 251) * errorMultiplier;	//	useless: 0 - 500, deadly: 0 - 10 (with 10 being *very* unlikely)
-          if (iPowerMod > iPowerModLimit)
-            iPowerMod = iPowerModLimit;
-
-          // In boxed mode, errors have quite more impact and are therefore cut down to be only two thirds
-          if (_global->bIsBoxed)
-            {
-              iAngleMod = (int)((double)iAngleMod / 3.0 * 2.0);
-              iPowerMod = (int)((double)iPowerMod / 3.0 * 2.0);
-            }
-
-          // 25 % to get a flattening anglemod (aka nearing to the ground)
-          if ((!(rand() % 4)) && (_targetAngle < 180))
-            iAngleMod *= -1; // right side angle
-          if ((rand() % 4) && (_targetAngle > 180))
-            iAngleMod *= -1; // right side angle (other way round, because here a positive mod is flattening!
-
-          // 25 % to get a lessening powermod. (it is more likely to overshoot than be too short!)
-          if (!(rand() % 4)) 		// (and it leads to too many stupid self
-            iPowerMod	*=	-1;	// hits to allow negative PoerMod too often
-
-          // The modification must not lead to a too stupid angle
-          while (	iAngleMod
-                  &&(	(	(_targetAngle < 180)
-                        &&(	((iAngleMod + _targetAngle) < 95)
-                            ||((iAngleMod	+	_targetAngle) > 175) ) )
-                      ||(	(_targetAngle >=180)
-                          &&(	((iAngleMod + _targetAngle) > 265)
-                              ||((iAngleMod	+	_targetAngle) < 185) ) )
-                    ) ) iAngleMod /= 2;
-          _targetAngle += iAngleMod;
-
-          // Same applies for Power:
-          while	(	iPowerMod
-                  &&(	((iPowerMod + _targetPower) > MAX_POWER)
-                      ||((iPowerMod + _targetPower) < (MAX_POWER * 0.1)) ) )
-            iPowerMod /= 2;
-
-          // iPowerMod needs to be a multiplier of 5:
-          iPowerMod -= iPowerMod % 5;
-
-          _targetPower += iPowerMod;
-
-#ifdef DEBUG_AIM
-          printf( "Error-Adjusting: Angle %3d Power %4d\n", iAngleMod, iPowerMod);
-#endif // DEBUG_AIM
-        }
-
-#ifdef DEBUG_AIM
-      cout << "HITTING " << _target->player->getName() << " ???" << endl << endl;
-#endif // DEBUG_AIM
-
-      // Last one: if we are revenging, tell em!
-      // Added _target check to avoid segfault
-      if  ( (tank->cw < WEAPONS) && (_target)                      // Wepaon selected?
-            && (weapon[tank->cw].damage > 1)                       // One that delivers damage?
-            && (revenge == _target->player)                        // And the target is our revengee?
-            && ((rand() % ((int)DEADLY_PLAYER + 2 - (int)type)))	) // And we do really want to taunt?
-        {
-          FLOATTEXT *DIEText;
-          char *my_text;
-          my_text = selectRetaliationPhrase();
-          DIEText = new FLOATTEXT (_global, _env, my_text, (int) tank->x, (int) tank->y - 30, color, CENTRE);
-          if (my_text) free(my_text);
-          if ( DIEText)
-          {
-              //DIEText->xv = 0;
-              //DIEText->yv = -0.4;
-              DIEText->set_speed(0.0, -0.4);
-              DIEText->maxAge = 150;
-          }
-          else
-              perror ( "player.cc: Failed allocating memory for DieText in calculateAttack().");
-        }
-      // As we might finish here because of a good overshoot, iTargettingRounds need to be maxed!
-      iTargettingRound = retargetAttempts;
-    }
-  return(0);
-}
-
-void PLAYER::calculateAttackValues(int &aDistanceX, int aDistanceY, int &aRawAngle, int &aAdjAngle, int &aPower, bool aAllowFlip)
-{
-  double dAirTime, dxTime;
-  double dSlopeX, dSlopeY;
-  double dAngleVariation, dPowerVariation;
-  bool bIsWrapped = false; // Special handling for wrapped walls
-
-  aDistanceX = _targetX - tank->x;
-
-  /* --- Step 1: find the raw angle, aka the obstacle-free optimal angle --- */
-
-  aRawAngle = (int)(atan2((double)aDistanceX, (double)(aDistanceY - abs (aDistanceX))) / PI * 180.0);
-
-  // Bring in Range:
-  if (aRawAngle < 0)		aRawAngle = aRawAngle + 360;
-  if (aRawAngle < 90)		aRawAngle = 135;
-  if (aRawAngle > 270)	aRawAngle = 225;
-
-  if ((_env->current_wallType == WALL_WRAP) && ((rand() % ((int)type + 1)) || !aAllowFlip))
-    {
-      // Note: We always wrap when possible and flipping not allowed, to have the shorter distance for sure!
-      int iWrapDistance = 0;
-
-      // if the distance through the wall is shorter, take it!
-      if (tank->x < _targetX)
-        iWrapDistance = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
-      else
-        iWrapDistance = tank->x -1 + _global->screenWidth - 2 - _targetX;
-
-      if (abs(iWrapDistance) < abs(aDistanceX))
-        {
-          bIsWrapped = true;
-          aDistanceX = iWrapDistance;
-          aRawAngle = 180 + (180 - aRawAngle); // flip!
-        }
-    }
-
-  // Angle variation for more flavour:
-  dAngleVariation = (rand() % 21) * focusRate; // useless: 0-4, deadly: 0-20
-  if (rand() % 2) dAngleVariation *= -1.0;
-  while	(	(dAngleVariation > 0.0)
-          &&(	((aRawAngle > 180) && (	((aRawAngle + dAngleVariation) < 190)
-                                      ||((aRawAngle + dAngleVariation) > 260)))
-              ||((aRawAngle < 180) && (	((aRawAngle + dAngleVariation) > 170)
-                                        ||((aRawAngle + dAngleVariation) < 100))) )	)
-    dAngleVariation /= 2.0;
-  aRawAngle += (int)dAngleVariation;
-
-  // Maybe we could switch sides?
-  if ((_env->current_wallType != WALL_STEEL) && (rand() % 2) && (rand() % (int)type) && aAllowFlip)
-    {
-      // Yes, we can! ( (char *)"Change is coming!" ;-) (b.h.o) )
-      aRawAngle = 180 + (180 - aRawAngle); // This switches sides, yes. :-D
-
-      if (tank->x < _targetX)
-        {
-          // We switch angle from right to left: (original distance is positive)
-          switch (_env->current_wallType)
-            {
-            case WALL_RUBBER:
-              // Add Distances to the wall to bounce from:
-              aDistanceX += 2 * (tank->x - 1);
-              break;
-            case WALL_SPRING:
-              // Spring walls add velocity, so adapt a bit
-              aDistanceX += tank->x - 1 + ((tank->x - 1) * 0.75);
-              break;
-            case WALL_WRAP:
-              // Distance is new: only distances from the wall:
-              if (bIsWrapped)
-                aDistanceX = tank->x -1 + _global->screenWidth - 2 - _targetX;
-              else
-                aDistanceX = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
-              break;
-            }
-        }
-      else
-        {
-          // We switch angle from left to right: (original distance is negative)
-          switch (_env->current_wallType)
-            {
-            case WALL_RUBBER:
-              // Add Distances to the wall to bounce from:
-              aDistanceX += 2 * (_global->screenWidth - tank->x - 2);
-              break;
-            case WALL_SPRING:
-              // Spring walls add velocity, so adapt a bit
-              aDistanceX += _global->screenWidth - tank->x - 2 + ((_global->screenWidth - tank->x - 2) * 0.75);
-              break;
-            case WALL_WRAP:
-              // Distance is new: only distances from the wall:
-              if (bIsWrapped)
-                aDistanceX = -1 * (tank->x -1 + _global->screenWidth - 2 - _targetX);
-              else
-                aDistanceX = tank->x -1 + _global->screenWidth - 2 - _targetX;
-              break;
-            }
-        }
-    }
-
-  /* --- Step 2: Adjust the Angle given clearance --- */
-
-  aAdjAngle = aRawAngle;
-  while ((aAdjAngle < 180) && !(tank->shootClearance(aAdjAngle)))
-    aAdjAngle++;
-  while ((aAdjAngle > 180) && !(tank->shootClearance(aAdjAngle)))
-    aAdjAngle--;
-
-  /* --- Step 3: Find neccessary Power --- */
-  dSlopeX = _global->slope[aAdjAngle][0];
-  dSlopeY = _global->slope[aAdjAngle][1];
-
-  if (dSlopeX != 0.0)
-    dxTime = ((double)aDistanceX / fabs(dSlopeX));
-  else
-    // entirely down to the elements now
-    dxTime = ((double)aDistanceX / 0.000001);
-
-  // Low target, less power
-  // xdistance proportional to sqrt(dy)
-  dAirTime = fabs(dxTime) + (((double)aDistanceY * dSlopeY) * _env->gravity * (100.0 / _global->frames_per_second)) * 2.0;
-
-  // Less airTime doesn't necessarily mean less power
-  // Horizontal firing means more power needed even though
-  //   airTime is minimised
-
-  aPower = (int)(sqrt (dAirTime * _env->gravity * (100.0 / _global->frames_per_second))) * 100;
-
-  // Power variation for more flavour:
-  dPowerVariation = (rand() % 51) * focusRate; // useless: 0-10, deadly: 0-50
-  dPowerVariation -= (int)dPowerVariation % 5;
-  if (rand() % 2) dPowerVariation *= -1.0;
-  aPower += dPowerVariation;
-
-  if (aPower > MAX_POWER)					aPower = MAX_POWER;
-  if (aPower < (MAX_POWER / 20))	aPower = MAX_POWER / 20;
-
-}
-
-int PLAYER::calculateDirectAngle (int dx, int dy)
-{
-  double angle;
-
-  angle = atan2 ((double)dx, (double)dy) / PI * 180;
-  angle += (rand () % 40 - 20) * errorMultiplier;
-
-  if (angle < 0)
-    angle = angle + 360;
-  if (angle < 90)
-    angle = 90;
-  else if (angle > 270)
-    angle = 270;
-
-  return ((int)angle);
-}
-
-TANK * PLAYER::computerSelectTarget (int aPreferredWeapon, bool aRotationMode)
-{
-  int random_target;
-  int attempts = 0;
-  int max_attempts = (int)type * 3;
-  TANK *best_target = NULL;
-  int current_score = 0;
-  int best_score = -1 * MAX_OVERSHOOT; // start with a loooooow score, so that even score<0 tanks can become best target
-  TANK *current_tank = NULL;
-  TANK *tankPool[10];
-  int iMoneyNeed = getMoneyToSave() - money;	// Are we in need for money?
-  int target_count = 0;
-
-#ifdef DEBUG
-  cout << " -> I need " << iMoneyNeed << " Credits *urgently*!" << endl;
-  if (aPreferredWeapon < WEAPONS)
-    cout << " -  Searching target for " << weapon[aPreferredWeapon].name << endl;
-  else
-    cout << " -  Searching target for " << item[aPreferredWeapon - WEAPONS].name << endl;
-#endif // DEBUG
-  // find out how many tries we have to find a good target
-
-  // Fill tankPool
-  for (int i = 0; i < _global->numPlayers; i++)
-    {
-      if ( (_global->players[i]) && (_global->players[i]->tank) )
-      {
-        tankPool[i] = _global->players[i]->tank;
-        target_count++;
-      }
-      else
-        tankPool[i] = NULL;
-    }
-
-  if (target_count < 2)     // just us left or nobody
-     return NULL;
-
-  // who do we want to shoot at?
-  while (attempts < max_attempts)
-    {
-      // select random tank for target
-      if (_global->numPlayers > 0)
-          random_target = rand() % _global->numPlayers;
-      else
-          random_target = 0;
-      current_tank	=	tankPool[random_target];
-
-      if (current_tank)
-        {
-          current_score	= 0;
-          // only consider living tanks that are not us
-
-          if ( (current_tank->l > 0) && (current_tank->player != this))
-            {
-              int iDamage		= 0;
-
-              if (weapon[aPreferredWeapon].numSubmunitions > 1)
-                iDamage	=	(damageMultiplier
-                           * weapon[weapon[aPreferredWeapon].submunition].damage
-                           * (weapon[aPreferredWeapon].numSubmunitions / 3.0));
-              else
-                iDamage	=	(damageMultiplier
-                           * weapon[aPreferredWeapon].damage
-                           * weapon[aPreferredWeapon].spread);
-
-              // compare the targets strength to ours
-              int iDiffStrength =	( (tank->l + tank->sh) - (current_tank->l + current_tank->sh));
-
-              current_score = iDiffStrength;
-
-              if (iDiffStrength < 0)
-                {
-                  // The target is stronger. Are we impressed?
-                  if ( (defensive < 0.0) && ( (rand() % ( (int) type + 1))))
-                    // No we aren't, add defensive-modified Strength
-                    // (The more offensive, the less impressed we are)
-                    current_score += (int) ( ( (defensive - 3.0) / 2.0) * (double) iDiffStrength);
-                }
-              else
-                // the target is weaker, add points modified by how defensive we are
-                current_score	+=	(int) ( (double) iDiffStrength * ( (defensive + 3.0) / 2.0));
-#ifdef DEBUG
-              cout << " (str)" << current_score;
-#endif
-              // check to see if we are on the same team
-              switch ( (int) team)
-                {
-                case TEAM_JEDI:
-                  if ((current_tank->player->team == TEAM_JEDI) && !aRotationMode)
-                    current_score -= 500 * (int) type;
-                  if ((current_tank->player->team == TEAM_JEDI) && aRotationMode)
-                    current_score -= MAX_OVERSHOOT; // no team consideration in rotation mode!
-
-                  if (current_tank->player->team == TEAM_SITH)
-                    current_score	+= -200.0 * (defensive - 2.0) * ( (double) type / 2.0);
-                  break;
-
-                case TEAM_SITH:
-                  if ((current_tank->player->team == TEAM_SITH) && !aRotationMode)
-                    current_score -= 500 * (int) type;
-                  if ((current_tank->player->team == TEAM_SITH) && aRotationMode)
-                    current_score -= MAX_OVERSHOOT; // no team consideration in rotation mode!
-
-                  if (current_tank->player->team == TEAM_JEDI)
-                    current_score	+= -200.0 * (defensive - 2.0) * ( (double) type / 2.0);
-                  break;
-/*
-                default:
-                  // Neutrals go rather for sith than jedi. (but not much)
-                  if (current_tank->player->team == TEAM_JEDI)
-                    current_score += -25.0 * (defensive - 2.0) * ( (double) type / 2.0);
-
-                  if (current_tank->player->team == TEAM_SITH)
-                    current_score	+= -50.0 * (defensive - 2.0) * ( (double) type / 2.0);
-*/
-                }
-#ifdef DEBUG
-              cout << " (team)" << current_score;
-#endif // DEBUG
-              // do we have a grudge against the target
-              if (current_tank->player == revenge)
-                {
-                  /*
-                  switch ( (int) team)
-                    {
-                    case TEAM_JEDI:
-                      // Revenge is a dark force!
-                      current_score += (50 * ( (defensive - 1.5) * -1.0));
-                      break;
-                    case TEAM_SITH:
-                      // Revenge means power!
-                      current_score += (200 * ( (defensive - 1.5) * -1.0));
-                      break;
-                    default:
-                   */
-                      current_score += (100 * ( (defensive - 1.5) * -1.0));
-                 //   }
-                }
-#ifdef DEBUG
-              cout << " (rev)" << current_score;
-#endif // DEBUG
-              // prefer targets further away when violent death is on
-              if (_global->violent_death)
-                {
-                  int distance;
-                  distance = (int) fabs (tank->x - current_tank->x);
-
-                  if (distance > _global->halfWidth)
-                    current_score += 100.0 * ( (defensive + 3.0) / 2.0);
-                }
-#ifdef DEBUG
-              cout << " (dis)" << current_score;
-#endif // DEBUG
-              // Add some points if the target is more intelligent than we are (get'em DOWN!)
-              // or substract if we are the better one. (Deal with the nutter later...)
-              if ( (current_tank->player->type != HUMAN_PLAYER)
-                   && (current_tank->player->type	<	DEADLY_PLAYER))
-                current_score +=	50 *	( (int) current_tank->player->type - (int) type);
-              else
-                current_score	+=	50 *	( (int) DEADLY_PLAYER - (int) type);
-              // Players, last player type and part time bots are counted as deadly bots.
-#ifdef DEBUG
-              cout << " (typ)" << current_score;
-#endif // DEBUG
-              // Add points for score difference if they have more than us
-              // useless bot: 1 * diff * 60 --> 4 points difference would mean +240 score
-              // deadly bot : 3 * diff * 60 --> 4 points difference would mean +720 score
-              if (current_tank->player->score > score)
-                current_score	+=	( (int) type + 1) / 2 * (current_tank->player->score - score) * 60;
-
-#ifdef DEBUG
-              cout << " (scr)" << current_score;
-#endif // DEBUG
-              if (aPreferredWeapon < WEAPONS)
-                {
-                  // As we are wanting to fire a weapon, add points, if the damage is greater than the targets health
-                  // (if we are in need for money, but not on the same team)
-                  int iDamageDiff = iDamage - (current_tank->l + current_tank->sh);
-
-                  if ( (iDamageDiff > 0) && (iMoneyNeed > 0)
-                       &&( ( team == TEAM_NEUTRAL )
-                           ||((team != TEAM_NEUTRAL) && (current_tank->player->team != team))  ) )
-                    current_score += (double) iDamageDiff * (1.0 + ( (defensive + (double) type) / 10.0));
-#ifdef DEBUG
-                  cout << " (dmg)" << current_score;
-#endif // DEBUG
-                  // Check whether the target is buried, and substract points for non-burrower/-penetrator
-                  int iBurylevel = current_tank->howBuried();
-
-                  if (iBurylevel > BURIED_LEVEL)
-                    {
-                      // The target is burried!
-                      if	( ( (aPreferredWeapon < BURROWER) || (aPreferredWeapon > PENETRATOR))
-                           && ( (aPreferredWeapon < TREMOR) || (aPreferredWeapon	> TECTONIC)))
-                        {
-                          // Napalm and shaped charges are absolutely useless
-                          if	( ( (aPreferredWeapon >= SML_NAPALM) && (aPreferredWeapon <= LRG_NAPALM))
-                               || ( (aPreferredWeapon >= SHAPED_CHARGE) && (aPreferredWeapon <= CUTTER)))
-                            current_score	-=	(int) type * 500; // Even the useless bot isn't *that* stupid
-                          else
-                            {
-                              // For all other weapons we go for the radius of the blast
-                              if (iBurylevel < weapon[aPreferredWeapon].radius)
-                                current_score *= 1.0 - ( ( (double) iBurylevel / (double) weapon[aPreferredWeapon].radius) / 2.0);
-                              else
-                                current_score	-=	(double) iDamage * (double) type * ( (double) defensive + 3.0);
-                            }
-                        }
-                      else
-                        // As we *want* to fire an appropriate weapon, the target looks rather nice to us!
-                        current_score += ( (double) (iBurylevel - BURIED_LEVEL) / ( ( (double) type + 1.0) / 2.0)) * (double) iDamage;
-#ifdef DEBUG
-                      cout << " (bur)" << current_score;
-#endif // DEBUG
-                    }
-
-                  // Finally, for weapons, see, if we can do good blast damage!
-                  if (type >= RANGEFINDER_PLAYER)
-                    {
-                      int iBlastBonus = 0;
-                      iBlastBonus = (int) ( (1.0 - ( (double) type / 10.0))
-                                            * getBlastValue (current_tank, weapon[aPreferredWeapon].damage, aPreferredWeapon)
-                                            * ( (defensive - 3.0) / -2.0));
-
-                      if (iBlastBonus > 0)
-                        {
-                          current_score	+=	iBlastBonus;
-                          // if we need money, blast bonus is valued higher:
-
-                          if (iMoneyNeed > 0)
-                            current_score	+=	iBlastBonus * ( (double) type / 2.0);
-                        }
-                    }
-                }
-
-              if (aRotationMode && ((team == TEAM_NEUTRAL) || (team != current_tank->player->team)))
-                {
-                  // In rotationmode we try to actually reach the target with the preferred weapon
-                  tank->cw	= aPreferredWeapon;
-                  _target 	= current_tank;
-                  _targetX	=	current_tank->x;
-                  _targetY	=	current_tank->y;
-                  iTargettingRound = 0;
-                  int iOvershoot = abs(calculateAttack(current_tank));
-                  if (iOvershoot > _global->screenWidth)
-                    current_score = -1 * MAX_OVERSHOOT; // Wall-Hit! Target is unreachable!
-                  else
-                    current_score -=  iOvershoot;	// substract overshoot!
-#ifdef DEBUG
-                  cout << " (rot)" << current_score;
-#endif // DEBUG
-                }
-
-#ifdef DEBUG
-              cout << " => " << current_score<< " : " << current_tank->player->getName() << endl;
-#endif // DEBUG
-
-              // decide if this target is better than others
-              if ((current_score > best_score) || (!aRotationMode && !best_target))
-                {
-                  best_score = current_score;
-                  best_target = current_tank;
-                }
-              attempts++;
-            }
-        }     // end of if we have a valid tank
-    }
-
-  if (best_target)
-    {
-      _target = best_target;
-      if (_target)
-      {
-        _targetX= _target->x;
-        _targetY= _target->y;
-#ifdef DEBUG
-        cout << " -> " << best_target->player->getName() << " wins! (";
-        cout << best_target->l << " life, " << best_target->sh << " shield)" << endl;
-#endif // DEBUG
-      }
-    }
-#ifdef DEBUG
-  else
-    cout << " -> Unable to find target!!!" << endl;
-#endif // DEBUG
-
-  return (best_target);
-}
-
-int PLAYER::getUnburyingTool()
-{
-  int iTool = 0; // small missile, if nothing else fits
-  if (nm[LRG_LAZER])			iTool = LRG_LAZER;
-  if (nm[MED_LAZER])			iTool = MED_LAZER;
-  if (nm[SML_LAZER])			iTool = SML_LAZER;
-  if (ni[ITEM_TELEPORT])	iTool = WEAPONS + ITEM_TELEPORT;
-  if (ni[ITEM_SWAPPER])		iTool = WEAPONS + ITEM_SWAPPER;
-  if (nm[HVY_RIOT_BOMB])	iTool = HVY_RIOT_BOMB;
-  if (nm[RIOT_BOMB])			iTool = RIOT_BOMB;
-  if (nm[RIOT_CHARGE])		iTool = RIOT_CHARGE;
-  if (nm[RIOT_BLAST])			iTool = RIOT_BLAST;
-  return(iTool);
-}
-
-int PLAYER::computerSelectItem ()
-{
-  int current_weapon = 0; // Current Weapon (defaults to small missile)
-  int iWeaponPool[15];
-  int iPoolSize		=	(int)type * 3;
-  int count 			= 0;
-  // Initialize targetting:
-  _target = NULL;
-  _targetX= 0;
-  _targetY= 0;
-
-#ifdef DEBUG
-  cout << getName() << " : Starting target and weapon evaluation..." << endl;
-  if (defensive < -0.9) cout << "(True Offensive)" << endl;
-  if ((defensive >=-0.9) && (defensive < -0.75)) cout << "(Very Offensive)" << endl;
-  if ((defensive >=-0.75) && (defensive < -0.25)) cout << "(Offensive)" << endl;
-  if ((defensive >=-0.25) && (defensive < 0.00)) cout << "(Slightly Offensive)" << endl;
-  if (defensive == 0.0)	cout << "(Neutral)" << endl;
-  if ((defensive >0.0) && (defensive <= 0.25)) cout << "(Slightly Defensive)" << endl;
-  if ((defensive >0.25) && (defensive <= 0.75)) cout << "(Defensive)" << endl;
-  if ((defensive >0.75) && (defensive <= 0.9)) cout << "(Very Defensive)" << endl;
-  if (defensive > 0.9)	cout << "(True Defensive)" << endl;
-  cout << "----------------------------------" << endl;
-#endif // DEBUG
-  // 1.: Preselection if buried
-
-  if (tank->howBuried () > BURIED_LEVEL)
-    {
-      current_weapon = getUnburyingTool();
-#ifdef DEBUGctank
-      if (current_weapon < WEAPONS)
-        cout << "I have chosen a \"" << weapon[current_weapon].name << "\" to free myself first!" << endl;
-      else
-        cout << "I have chosen a \"" << item[current_weapon - WEAPONS].name << "\" to free myself first!" << endl;
-#endif // DEBUG
-    }
-  else
-    {
-      // 2.: Determine iPoolSize
-      if (iPoolSize > 15)
-        iPoolSize = 15; // Or part-time-bots would bust array size!
-
-      // 3.: Fill iWeaponPool
-      iWeaponPool[0] = 0;	// The Small missile is always there!
-      count					=	1;	// ...so start from second slot!
-
-      while (count < iPoolSize)
-        {
-          int i	=	0;
-          // bots get a number of tries depending on their intelligence
-          current_weapon	=	0;
-          while (!current_weapon && (i < ( (int) type * 2)))
-            {
-              current_weapon = Select_Random_Weapon();
-              if (! current_weapon)
-                current_weapon = Select_Random_Item();
-              if	( (current_weapon	>=	THINGS)	//should never occur, but make it sure!
-                   || ( (current_weapon < WEAPONS) && (!nm[current_weapon]))
-                   || ( (current_weapon >= WEAPONS) && (!ni[current_weapon - WEAPONS])))
-                current_weapon = 0;
-              i++;
-            }
-
-          if (!current_weapon && (count > 1))
-            {
-              // Slot 0 is allways the small missile, slot 1 is always the first thing the bot "things" of
-              // "Last Resort" switching takes place from slot 2 on
-              if (nm[MED_MIS])	current_weapon	=	MED_MIS;
-
-              // Only bots with 4+ slots (rangefinder and up) revert to the large missile
-              if ( (count > 2) &&	nm[LRG_MIS])	current_weapon	= LRG_MIS;
-            }
-          iWeaponPool[count]	=	current_weapon;
-          count++;
-        }
-
-      // 4a.: check if a dirtball is chosen
-      if ( (iWeaponPool[1] >= DIRT_BALL) && (iWeaponPool[1] <= SUP_DIRT_BALL))
-        current_weapon	=	iWeaponPool[1];
-      else
-        {
-          // 4b.: Sort iWeaponPool, so that the most liked weapon is first
-          //  (...if the bot doesn't "forget" to sort ...)
-          if (rand() % ( (int) type + 1))
-            {
-              bool bIsSorted = false;
-              while (!bIsSorted)
-                {
-                  bIsSorted	=	true;
-                  // The bot does only sort the first few weapons (type+1)
-                  // Stupid: first two, deadly: first 6
-                  for (int i = 1; i < ( (int) type + 1); i++)
-                    {
-                      if (_weaponPreference[iWeaponPool[i-1]] < _weaponPreference[iWeaponPool[i]])
-                        {
-                          bIsSorted	=	false;
-                          current_weapon	=	iWeaponPool[i-1];
-                          iWeaponPool[i-1] = iWeaponPool[i];
-                          iWeaponPool[i]	=	current_weapon;
-                        }
-                    }
-                }
-            }
-          current_weapon	=	iWeaponPool[0]; // Most liked weapon, or, if not sorted, the small missile
-          // Having the small missile here means, that the bot is selecting the most easy target.
-          // Obviously that means, that the more stupid a bot is, the more often it will go for the easy strike.
-        }
-#ifdef DEBUG
-      for (int i = 0; i < iPoolSize; i++)
-        {
-          cout << i << ".: ";
-          printf( "% 5d Pref - ", _weaponPreference[iWeaponPool[i]]);
-          if (iWeaponPool[i] < WEAPONS)
-            cout << "\"" << weapon[iWeaponPool[i]].name << "\"" << endl;
-          else
-            cout << "\"" << item[iWeaponPool[i] - WEAPONS].name << "\"" << endl;
-        }
-#endif // DEBUG
-      // if boxed mode is on, we have to try to find a target in rotation mode first!
-      if (	_global->bIsBoxed	&& (current_weapon < WEAPONS)
-           && ( (current_weapon <	DIRT_BALL)
-                ||(current_weapon >	SUP_DIRT_BALL)))
-        {
-          int i		=	0;
-          while ((i < iPoolSize) && !_target)
-            {
-              _target = computerSelectTarget(iWeaponPool[i], true);
-              i++;
-            }
-          if (_target)
-            current_weapon = iWeaponPool[i - 1];
-        }
-      if (!_target)               _target =	computerSelectTarget(current_weapon);
-      #ifdef OLD_GAMELOOP
-      if (!_target && _oldTarget) _target = _oldTarget;
-      #endif
-      if (!_target) return (0); // If there is no target available, we have nothing more to do.
-      _targetX				= _target->x;
-      _targetY				= _target->y;
-
-      // 5.: if a weapon is choosen, cycle through the pool
-      // to find the best fitting one
-      if ( (current_weapon < WEAPONS)
-           && ( (current_weapon <	DIRT_BALL)
-                || (current_weapon >	SUP_DIRT_BALL)))
-        {
-          int iBestWeapon		= current_weapon;
-          int iWeaponScore	= 0; 		// Score of the currently used weapon
-          int iBestWeapScr	=	-5000;// Best score calculated so far
-          double dDamageMod	=	0.0;	// modifier for how close to targets health
-          int iWeaponDamage	=	0;		// Calculate the (real) weapon damage
-          int iTargetLife   = _target->l + _target->sh;
-          int iBurylevel    = _target->howBuried();
-          for (int i = 0; i < iPoolSize; i++)
-            {
-              current_weapon	=	iWeaponPool[i];
-
-              // 1.: avoid trying to shoot below the tank's level with lasers
-              if ( (_targetY >= tank->y) &&
-                   ( (current_weapon >= SML_LAZER) && (current_weapon <= LRG_LAZER)))
-                iWeaponPool[i] = current_weapon = 0; // revert to small missile
-#ifdef DEBUG
-              if (current_weapon < WEAPONS)
-                cout << " -> \"" << weapon[current_weapon].name << "\" : ";
-              else
-                cout << " -> (ERROR!) \"" << item[current_weapon - WEAPONS].name << "\" : ";
-#endif // DEBUG
-              // 2.: The closer the weapon damage is to the target health, the:
-              //     - more points added if it kills
-              //     - less points substracted if it doesn't kill
-              
-              // avoid trying to use weapons we do not have
-              if ( ( current_weapon < 0) || (current_weapon >= WEAPONS) )
-                 current_weapon = 0;
-
-              if (weapon[current_weapon].numSubmunitions > 1)
-                iWeaponDamage	=	damageMultiplier
-                                * weapon[weapon[current_weapon].submunition].damage
-                                * (weapon[current_weapon].numSubmunitions / (defensive + 3.0)); // Clusters don't hit well (napalm?)
-              else
-                {
-                  if (weapon[current_weapon].spread > 1)
-                    iWeaponDamage	=	damageMultiplier
-                                    * weapon[current_weapon].damage
-                                    * (weapon[current_weapon].spread / (defensive + 2.0)); // Spreads *might* hit well, but do seldom
-                  else
-                    iWeaponDamage	=	damageMultiplier
-                                    * weapon[current_weapon].damage;
-                }
-
-              // The more intelligent and defensive a bot is, the more (char *)"savety bonus" is granted:
-              iWeaponDamage = (int) ( (double) iWeaponDamage / (1.0 + ( ( (double) type * (defensive + 2.0)) / 50.0)));
-
-              // Examples:
-              // Full offensive, useless: 1.0 + ((1.0 * 1.0) / 50.0) = 1.02 <== The damage is like 102% of the real damage
-              // Full defensive, deadly : 1.0 + ((5.0 * 3.0) / 50.0) = 1.30 <== The damage is like 130% of the real damage
-#ifdef DEBUG
-              cout << iWeaponDamage << " damage, ";
-#endif // DEBUG
-              if ( iTargetLife > iWeaponDamage)
-                {
-                  // The weapon is too weak, substract points. (Less if we are offensive)
-                  dDamageMod	=	(double) iWeaponDamage / (double) iTargetLife;
-                  if (defensive < 0)
-                    iWeaponScore = (10 - (int) type) * (int) ( (-10.0 * (1.1 + defensive)) / dDamageMod);
-                  else
-                    iWeaponScore = (10 - (int) type) * (int) (-10.0 / dDamageMod);
-#ifdef DEBUG
-                  cout << "weak: ";
-#endif // DEBUG
-                  /*	Example calculations:
-                  		Bot is deadly: (int)type = 5
-                  		Weapon does 25% damage of the targets health: dDamegeMod = 0.25
-                  		iWeaponScore = (10 - 5) * (-10 / 0.25) = 5 * -40   = -200
-                  		Weapon does 50% damage of the targets health: dDamegeMod = 0.5
-                  		iWeaponScore = (10 - 5) * (-10 / 0.5)  = 5 * -20   = -100
-                  		Weapon does 75% damage of the targets health: dDamegeMod = 0.75
-                  		iWeaponScore = (10 - 5) * (-10 / 0.75) = 5 * -13,3 = -66,5
-                  		Weapon does 95% damage of the targets health: dDamegeMod = 0.95
-                  		iWeaponScore = (10 - 5) * (-10 / 0.95) = 5 * -10,5 = -52,5 */
-                }
-              else
-                {
-                  // The weapon is strong enough, add points (More, if we are defensive)
-                  dDamageMod	=	(double) iTargetLife / (double) iWeaponDamage;
-                  if (defensive > 0)
-                    iWeaponScore	=	(int) type * (int) ( (100.0 * (1.0 + defensive)) * dDamageMod);
-                  else
-                    iWeaponScore	=	(int) type * (int) (100.0 * dDamageMod);
-#ifdef DEBUG
-                  cout << "strong: ";
-#endif // DEBUG
-                  /*	Example calculations:
-                  		Bot is deadly: (int)type = 5
-                  		Weapon does 105% damage of the targets health: dDamegeMod = 0.95
-                  		iWeaponScore = 5 * (100 * 0.95) = 5 * 95   = 475
-                  		Weapon does 125% damage of the targets health: dDamegeMod = 0.8
-                  		iWeaponScore = 5 * (100 * 0.8)  = 5 * 80   = 400
-                  		Weapon does 150% damage of the targets health: dDamegeMod = 0.67
-                  		iWeaponScore = 5 * (100 * 0.67) = 5 * 67   = 335
-                  		Weapon does 200% damage of the targets health: dDamegeMod = 0.5
-                  		iWeaponScore = 5 * (100 * 0.5)  = 5 * 50   = 250 */
-                }
-#ifdef DEBUG
-              cout << dDamageMod << " dMod -> ";
-#endif // DEBUG
-              // 3.: Check if the way for a laser is clear if choosen
-              if ( (current_weapon >= SML_LAZER) && (current_weapon <= LRG_LAZER))
-                {
-                  int iXlow, iXhigh, iX, iY;	// temp vars to calculate with
-                  int iRockAmount	=	0;	// How much mountain there is in between
-
-                  if (tank->x < _targetX)
-                    {
-                      iXlow	=	tank->x;
-                      iXhigh =	_targetX;
-                    }
-                  else
-                    {
-                      iXlow	=	_targetX;
-                      iXhigh =	tank->x;
-                    }
-
-                  for (iX = iXlow; iX < iXhigh; iX++)
-                    {
-                      iY = tank->y - ( (tank->y - _targetY) / (iXhigh - iX)); // y the laser will be on it's way
-                      if (_env->surface[iX] < iY)
-                        iRockAmount++;	// Rock in the way!
-                    }
-                  iWeaponScore	-=	(int) type * iRockAmount * 10.0;
-                }
-
-              // 4.: If the target is burried, add points if we are using an adequate weapon
-              if (iBurylevel > BURIED_LEVEL)
-                {
-                  // The target is burried!
-                  if	( ( (current_weapon < BURROWER) || (current_weapon > PENETRATOR))
-                       && ( (current_weapon < TREMOR) || (current_weapon	> TECTONIC)))
-                    {
-                      // Napalm and shaped charges are absolutely useless
-                      if	( ( (current_weapon >= SML_NAPALM) && (current_weapon <= LRG_NAPALM))
-                           || ( (current_weapon >= SHAPED_CHARGE) && (current_weapon <= CUTTER)))
-                        iWeaponScore	-=	(int) type * 500; // Even the useless bot isn't *that* stupid
-                      else
-                        {
-                          // For all other weapons we go for the radius of the blast
-                          if (iBurylevel < weapon[current_weapon].radius)
-                            iWeaponScore *= 1.0 - ( ( (double) iBurylevel / (double) weapon[current_weapon].radius) / 2.0);
-                          else
-                            iWeaponScore	-=	(double) iWeaponDamage * (double) type * ( (double) defensive + 3.0);
-                        }
-                    }
-                  else
-                    // As we *want* to fire an appropriate weapon, the target looks rather nice to us!
-                    iWeaponScore += ( (double) (iBurylevel - BURIED_LEVEL) / ( ( (double) type + 1.0) / 2.0)) * (double) iWeaponDamage;
-                }
-
-              // 5.: Substract points, if we are within the blast radius
-              // 6.: Check, whether other tanks are in the blast radius, and add points according
-              //     to the additional damage delivered
-              // Note: It seems to be paradox, that defensive bots add points for spread/cluster
-              //       weapons, but they a) buy onyl few of them and b) add only noticably points
-              //       if the collateral damage is enough to kill.
-              if (type >= RANGEFINDER_PLAYER)
-                iWeaponScore	+=	getBlastValue (_target, iWeaponDamage, current_weapon, dDamageMod);
-
-              // 7.: Try to hit this target with the weapon, and substract the overshoot
-              if ((type >= RANGEFINDER_PLAYER) && (current_weapon < WEAPONS))
-                {
-                  tank->cw = current_weapon;
-                  iTargettingRound = 0;
-                  iWeaponScore -= abs(calculateAttack(_target));
-                }
-
-              // 8.: Modify the Score by weapon preferences
-              if (iWeaponScore > 0)
-                iWeaponScore	=	(int) ( (double) iWeaponScore
-                                       * (1.0
-                                          + ( (double) _weaponPreference[current_weapon]
-                                              / (double) MAX_WEAP_PROBABILITY)));
-              // (it will only have a slight effect unless the prefs are really wide apart and the
-              //  original points very close to each other)
-#ifdef DEBUG
-              cout << iWeaponScore << " points!" << endl;
-#endif // DEBUG
-              // See if the choice fits better
-              if (iWeaponScore > iBestWeapScr)
-                {
-                  iBestWeapScr	=	iWeaponScore;
-                  iBestWeapon		=	current_weapon;
-                }
-            }
-          current_weapon	=	iBestWeapon;
-        }
-    }
-
-  // 6.: Kamikaze probability:
-  // The more stupid and offensive a bot is, the more chance he has
-  // to blow himself up when life is falling below a certain limit.
-  if	( ( (tank->l + tank->sh) < 30)
-       || ( ( (tank->l + tank->sh) < 50) && (tank->howBuried () > BURIED_LEVEL)))
-    {
-      // A buried bot needs to free himself first, leaving all others a free shot!
-      double dBlowMod = defensive + 2.0;	// gives 1.0 to 3.0
-      dBlowMod	*=	(double) type / 2.0;		// gives 1.0 to 9.0
-      dBlowMod	+=	1.0;									// gives 2.0 to 10.0
-      // dBlowMod is now between  2.0 <== useless full offensive bot (50% chance)
-      //                     and 10.0 <== deadly full defensive bots (10% chance)
-
-      if (! (rand() % (int) dBlowMod))
-        {
-          // okay, I'm a goner, so BANZAAAAIIII! (maybe, check for a way first)
-          int iWeaponDamage = 0;
-          current_weapon	=	0;
-          if (nm[NUKE])							current_weapon	=	NUKE;
-          if (nm[DTH_SPREAD])				current_weapon	=	DTH_SPREAD;
-          if (nm[DTH_HEAD])					current_weapon	=	DTH_HEAD;
-          if (nm[ARMAGEDDON])				current_weapon	=	ARMAGEDDON;
-          if (nm[CUTTER])						current_weapon	=	CUTTER;
-          if (ni[ITEM_VENGEANCE])		current_weapon	=	ITEM_VENGEANCE		+	WEAPONS;
-          if (ni[ITEM_DYING_WRATH])	current_weapon	=	ITEM_DYING_WRATH	+	WEAPONS;
-          if (ni[ITEM_FATAL_FURY])	current_weapon	=	ITEM_FATAL_FURY		+	WEAPONS;
-          if (current_weapon < WEAPONS)
-            iWeaponDamage = weapon[current_weapon].damage * damageMultiplier;
-          else
-            iWeaponDamage	=		weapon[ (int) item[current_weapon - WEAPONS].vals[0]].damage
-                             *	item[current_weapon - WEAPONS].vals[1]
-                             * damageMultiplier;
-
-          // Is something available, and do we take others with us?
-          if ( (current_weapon > 0) && (getBlastValue (tank, iWeaponDamage, current_weapon) > 0.0))
-            {
-              // Yieha! Here we go!
-#ifdef DEBUG
-              printf("I have chosen to go bye bye with a...\n");
-              if (current_weapon < WEAPONS)
-                printf("%s\n", weapon[current_weapon].name);
-              else
-                printf("%s\n", item[current_weapon - WEAPONS].name);
-#endif // DEBUG
-              _targetX = tank->x;
-              _targetY = tank->y;
-              FLOATTEXT *kamikazeText;
-              kamikazeText = new FLOATTEXT (_global, _env, selectKamikazePhrase(),
-                                            (int) tank->x, (int) tank->y - 30, color, CENTRE);
-              if ( kamikazeText)
-              {
-                // kamikazeText->xv = 0;
-                // kamikazeText->yv = -0.4;
-                kamikazeText->set_speed(0.0, -0.4);
-                kamikazeText->maxAge = 300;
-              }
-              else
-                  perror ( "player.cc: Failed allocating memory for kamikazeText in computerSelectItem.");
-            }
-        }
-    }
-
-#ifdef DEBUG
-  if (_target)
-    printf("Finished!\nShooting at %s\n", _target->player->getName() );
-  else
-    printf("Finished!\nI'll free myself.\n");
-  if (current_weapon < WEAPONS)
-    printf("Using weapon %s\n", weapon[current_weapon].name);
-  else
-    printf("Using item %s\n", item[current_weapon - WEAPONS].name);
-  printf("=============================================\n");
-#endif // DEBUG
-  tank->cw = current_weapon;
-  _global->updateMenu = 1;
-  return (current_weapon);
-}
-
-int PLAYER::computerControls ()
-{
-  int status = 0;
-  // At the most basic: select target, select weapon, aim and fire
-  tank->requireUpdate ();
-  if (_turnStage == SELECT_WEAPON)
-    {
-      computerSelectItem();
-      iTargettingRound = 0;
-      _turnStage = CALCULATE_ATTACK;
-      _global->updateMenu = 1;
-    }
-  else if (_turnStage == SELECT_TARGET)
-    {
-      cout << "ERROR: _turnstage became SELECT_TARGET!" << endl;
-      // Target is already chosen by computerSelectItem()
-      _turnStage = SELECT_WEAPON;
-    }
-  else if (_turnStage == CALCULATE_ATTACK)
-    {
-
-#ifdef DEBUG_AIM_SHOW
-      _global->bASD = true; // Now it is allowed to be true
-#endif
-
-      calculateAttack(NULL);
-      _turnStage = AIM_WEAPON; // If the targetting wasn't finished, it will be done later!
-
-#ifdef DEBUG_AIM_SHOW
-      _global->bASD = false; // And now it isn't!
-#endif
-    }
-  else if (_turnStage == AIM_WEAPON)
-    {
-      // First: Determine whether there are any updates to be done yet:
-      bool bDoAngleUpdate = false;
-      bool bDoPowerUpdate = false;
-      if (iTargettingRound == retargetAttempts)
-        {
-          // Do both when finished aiming
-          bDoAngleUpdate = true;
-          bDoPowerUpdate = true;
-        }
-      else if (iTargettingRound && (abs(_targetAngle - tank->a) <= 90))
-        // Only do Angle update if still aiming and difference isn't too high
-        bDoAngleUpdate = true;
-
-      if (bDoAngleUpdate && (_targetAngle > tank->a && tank->a < 270) )
-        {
-          // Left (If already aimed)
-          tank->a++;
-          _global->updateMenu = 1;
-        }
-      else if (bDoAngleUpdate && (_targetAngle < tank->a && tank->a > 90) )
-        {
-          // Right (if already aimed)
-          tank->a--;
-          _global->updateMenu = 1;
-        }
-      else if (bDoPowerUpdate && (_targetPower < (tank->p - 3) && tank->p > 0))
-        {
-          // Reduce power (if targetting is finished)
-          tank->p -= 5;
-          _global->updateMenu = 1;
-        }
-      else if (bDoPowerUpdate && (_targetPower > (tank->p + 3) && tank->p < MAX_POWER) )
-        {
-          // Increase Power (if targetting is finished)
-          tank->p += 5;
-          _global->updateMenu = 1;
-        }
-      else
-        {
-          // Targetting finished?
-          if (iTargettingRound == retargetAttempts)
-            _turnStage = FIRE_WEAPON; // Finished aiming, go ahead!
-          else
-            _turnStage = CALCULATE_ATTACK; // Not finished, do some more aiming
-        }
-    }
-  #ifdef OLD_GAMELOOP
-  else if (fi)
-    {
-      // if (fi) don't do any of the following
-    }
-  #endif
-  else if (_turnStage == FIRE_WEAPON)
-    {
-      // tank->activateCurrentSelection ();
-      _global->updateMenu = 1;
-      #ifdef DEBUG
-      printf("About to activate weapon.\n");
-      #endif
-      tank->simActivateCurrentSelection();
-      if (type == VERY_PART_TIME_BOT)
-          type = NETWORK_CLIENT;
-      #ifdef DEBUG
-      printf("Weapon was activated.\n");
-      #endif
-      gloating = false;
-      _turnStage = 0;
-      status = CONTROL_FIRE;
-    }
-  return (status);
-}
-
-int PLAYER::humanControls ()
-{
-  int moved = 0;
-  int status = 0;
-
-  if (tank)
-    {
-      if (!_env->mouseclock && mouse_b & 1 && mouse_x >= 250
-          && mouse_x < 378 && mouse_y >= 11 && mouse_y < 19)
-        {
-          _global->updateMenu = 1;
-          if (tank->fs)
-            {
-              tank->sht++;
-            }
-          tank->fs = 1;
-          if (tank->sht > SHIELDS - 1)
-            {
-              tank->sht = 0;
-            }
-        }
-      if (!_env->mouseclock && mouse_b & 1 && mouse_x >= 250
-          && mouse_x < 378 && mouse_y >= 21
-          && mouse_y < 29 && tank->player->ni[tank->sht] > 0 && (tank->fs || tank->sh > 0))
-        {
-          _global->updateMenu = 1;
-          tank->ds = tank->sht;
-          tank->player->ni[tank->sht]--;
-          tank->sh = (int)item[tank->sht].vals[SHIELD_ENERGY];
-        }
-
-      tank->requireUpdate ();
-    }
-
-  //Keyboard control
-  if ( _env->stage == STAGE_AIM)
-    {
-      if (tank)
-        {
-          if ( (key[KEY_LEFT] || key[KEY_A]) && !ctrlUsedUp && tank->a < 270)
-            {
-              tank->a++;
-              _global->updateMenu = 1;
-              if (key_shifts & KB_CTRL_FLAG)
-                ctrlUsedUp = TRUE;
-            }
-          if ( (key[KEY_RIGHT] || key[KEY_D]) && !ctrlUsedUp && tank->a > 90)
-            {
-              tank->a--;
-              _global->updateMenu = 1;
-              if (key_shifts & KB_CTRL_FLAG)
-                ctrlUsedUp = TRUE;
-            }
-          if ( (key[KEY_DOWN] || key[KEY_S]) && !ctrlUsedUp && tank->p > 0)
-            {
-              tank->p -= 5;
-              _global->updateMenu = 1;
-              if (key_shifts & KB_CTRL_FLAG)
-                ctrlUsedUp = TRUE;
-            }
-          if ( (key[KEY_UP] || key[KEY_W]) && !ctrlUsedUp && tank->p < MAX_POWER)
-            {
-              tank->p += 5;
-              _global->updateMenu = 1;
-              if (key_shifts & KB_CTRL_FLAG)
-                ctrlUsedUp = TRUE;
-            }
-          if ( (key[KEY_PGUP] || key[KEY_R]) && !ctrlUsedUp && tank->p < MAX_POWER )
-            {
-              tank->p += 100;
-              if (tank->p > MAX_POWER)
-                tank->p = MAX_POWER;
-              _global->updateMenu = 1;
-              if (key_shifts & KB_CTRL_FLAG)
-                ctrlUsedUp = TRUE;
-            }
-          if ( (key[KEY_PGDN] || key[KEY_F]) && !ctrlUsedUp && tank->p > 0)
-            {
-              tank->p -= 100;
-              if (tank->p < 0)
-                tank->p = 0;
-              _global->updateMenu = 1;
-              if (key_shifts & KB_CTRL_FLAG)
-                ctrlUsedUp = TRUE;
-            }
-        }
-    }
+/// Static helper values to help with keyboard controls:
+static bool ctrlUsedUp        = false;
+static bool has_ctrl_pressed  = false;
+static bool has_shift_pressed = false;
 
-    #ifdef OLD_GAMELOOP
-    if (k && !fi)
-    #endif
-    #ifdef NEW_GAMELOOP
-    // if ( keypressed() )
-    //    k = readkey();
-    // else
-    //   k = 0;
-    if (! k)
-    {
-       if ( keypressed() )
-         k = readkey();
-    }
-    if (k)
-    #endif
-    {
-      status = CONTROL_PRESSED;
-      if ( _env->stage == STAGE_AIM)
-        {
-          if (tank)
-            {
-              if (k >> 8 == KEY_N)
-                {
-                  tank->a = 180;
-                  _global->updateMenu = 1;
-                }
-              if ( (k >> 8 == KEY_TAB) || (k >> 8 == KEY_C) )
-                {
-                  _global->updateMenu = 1;
-                  while (1)
-                    {
-                      tank->cw++;
-                      if (tank->cw >= THINGS)
-                        tank->cw = 0;
-                      if (tank->cw < WEAPONS)
-                        {
-                          if (tank->player->nm[tank->cw])
-                            break;
-                        }
-                      else
-                        {
-                          if (item[tank->cw - WEAPONS].selectable && tank->player->ni[tank->cw - WEAPONS])
-                            break;
-
-                        }
-                    }
-                  //calcDamageMatrix (tank, tank->cw);
-                  //selectTarget ();
-                  changed_weapon = false;
-                }
-              if ( ( k >> 8 == KEY_BACKSPACE) || ( k >> 8 == KEY_Z) )
-                {
-                  _global->updateMenu = 1;
-                  while (1)
-                    {
-                      tank->cw--;
-                      if (tank->cw < 0)
-                        tank->cw = THINGS - 1;
-
-                      if (tank->cw < WEAPONS)
-                        {
-                          if (tank->player->nm[tank->cw])
-                            break;
-                        }
-                      else
-                        {
-                          if (item[tank->cw - WEAPONS].selectable && tank->player->ni[tank->cw - WEAPONS])
-                            break;
-
-                        }
-                    }
-                  changed_weapon = false;
-                }
-
-              // put the tank under computer control
-              if (k >> 8 == KEY_F10) 
-                {
-                  type = PART_TIME_BOT;
-                  setComputerValues();
-                  return (computerControls());
-                }
-
-              // move the tank
-              if (k >> 8 == KEY_COMMA) // || (key >> 8 == KEY_H) )
-                moved = tank->Move_Tank(DIR_LEFT);
-              else if (k >> 8 == KEY_H)
-                moved = tank->Move_Tank(DIR_LEFT);
-
-              if (k >> 8 == KEY_STOP) // || (key >> 8 == KEY_J) )
-                moved = tank->Move_Tank(DIR_RIGHT);
-              else if (k >> 8 == KEY_J)
-                moved = tank->Move_Tank(DIR_RIGHT);
-              if (moved)
-                _global->updateMenu = 1;
-
-              if ((k >> 8 == KEY_SPACE) &&
-                  (((tank->cw < WEAPONS) && (tank->player->nm[tank->cw] > 0)) ||
-                   ((tank->cw < THINGS) && (tank->player->ni[tank->cw - WEAPONS] > 0))))
-                {
-                  //	tank->activateCurrentSelection ();
-                  tank->simActivateCurrentSelection();
-                  gloating = false;
-                  status = CONTROL_FIRE;
-                }
-            }
-        }
-      if ((_env->stage == STAGE_ENDGAME) && (k >> 8 == KEY_ENTER || k >> 8 == KEY_ESC || k >> 8 == KEY_SPACE))
-        return (-1);
-    }
-  return (status);
-}
+/// @brief default ctor
+PLAYER::PLAYER() :
+	sdi_has_fired(ATOMIC_VAR_INIT(false))
+{
+	// Do a memset initialization thanks to VC++
+	memset(ni,           0, sizeof(int32_t) * ITEMS);
+	memset(nm,           0, sizeof(int32_t) * WEAPONS);
+	memset(currPref,     0, sizeof(int32_t) * THINGS);
+	memset(desired,      0, sizeof(int32_t) * THINGS);
+	memset(saveMoneyFor, 0, sizeof(int32_t) * THINGS);
+	memset(name,         0, sizeof(char)    * NAME_LEN);
+	memset(weapPref,     0, sizeof(int32_t) * THINGS);
+
+	nm[0] = 99; // need some small missiles. ;)
+	strncpy(name, "New Player", NAME_LEN);
+
+	// 25% of time set to perplay weapon preferences
+	preftype = (rand () % 4) ? ALWAYS_PREF : PERPLAY_PREF;
+
+	/* Generate a set of preferences now. The reason is:
+	 * If the player is a PERPLAY_PREF type player, no preferences
+	 * are loaded from the atanks configuration file. But if the user
+	 * changes the type to ALWAYS_PREF and starts a new game, no new
+	 * preferences are generated. So then these are used, which is
+	 * safe enough.
+	 */
+	generatePreferences();
+
+	switch (rand() % 4) {
+		case 0: // === red type ===
+			color = makecol(200 + (rand() % 56), rand() % 25, rand() % 25);
+		break;
+		case 1: // === green type ===
+			color = makecol( rand() % 25, 200 + (rand() % 56), rand() % 25);
+		break;
+		case 2: // === blue type ===
+			color = makecol( rand() % 25, rand() % 25, 200 + (rand() % 56) );
+		break;
+		case 3:
+		default: // === violet type ===
+			color = makecol( 200 + (rand() % 56), rand() % 25, 200 + (rand() % 56));
+		break;
+	}
 
+}
 
 
-// returns a static string to the player's team name
-char *PLAYER::Get_Team_Name()
+/// @brief default dtor
+PLAYER::~PLAYER ()
 {
-  char *team_name = "";
-
-  switch ( (int) team)
-    {
-    case TEAM_JEDI:
-      team_name = "Jedi";
-      break;
-    case TEAM_NEUTRAL:
-      team_name = "Neutral";
-      break;
-    case TEAM_SITH:
-      team_name = "Sith";
-      break;
-    }
-
-  return team_name;
+	if (tank) {
+		delete(tank);
+		tank = nullptr;
+	}
+
+	if (opponents) {
+		delete [] opponents;
+		opponents = nullptr;
+	}
 }
 
 
-
-// Pick a weapon to fire at random.
-// The weapon number is returned. If no other
-// weapon is in inventory, then 0 - small missile is
-// used.
-int PLAYER::Select_Random_Weapon()
+/// @brief update currPrefs array with considering needs and stock amounts
+void PLAYER::boostPrefences(bool boostArmour, bool boostAmps, bool boostWeapons)
 {
-  int index;
-  int num_weapons = 0;
-  int random_weapon;
-
-  // count number of different weapons we have
-  for (index = 1; index < WEAPONS; index++)
-    {
-      if ( nm[index] )
-        num_weapons++;
-    }
+	int32_t ai_level = static_cast<int32_t>(type);
+
+	for (int32_t i = 1; i < THINGS; ++i) {
+		double pref = currPref[i];
+
+		// boost preferences if wanted:
+		if (boostArmour
+		  && ( (WEAPONS + ITEM_ARMOUR  ) <= i)
+		  && ( (WEAPONS + ITEM_PLASTEEL) >= i) )
+			pref *= 1. + ((1. + static_cast<double>(RAND_AI_0P)) / 10.);
+
+		if (boostAmps
+		  && ( (WEAPONS + ITEM_INTENSITY_AMP) <= i)
+		  && ( (WEAPONS + ITEM_VIOLENT_FORCE) >= i) )
+			pref *= 1. + ((1. + static_cast<double>(RAND_AI_0P)) / 10.);
+
+		if (boostWeapons && i && (i < WEAPONS))
+			pref *= 1. + ((1. + static_cast<double>(RAND_AI_1P)) / 10.);
+
+		// Lower weapon preferences if there are enough in stock already
+		if (i && (i < WEAPONS)) {
+			double cur_amount = nm[i] / weapon[i].getDelayDiv();
+			double one_amount = weapon[i].amt / weapon[i].getDelayDiv();
+			double max_amount = one_amount * ai_level;
+			double div_amount = cur_amount - max_amount;
+
+			// - cur_amount is the total amount of single shots. getDelayDiv()
+			// is used, because it simply returns the number of shots fired by
+			// delayed weapons, while it returns always 1 for the other weapons.
+			// - one_amount - The number of nm[i] that is gotten by buying one
+			//                unit.
+			// - max_amount - below this no reduction or sale is considered.
+			// - div_amount - if positive, the bot has enough in stock.
+			//              - if larger than one_amount, selling the excess
+			//                amount is considered.
+
+			if (div_amount >= 1.) {
+				pref /= div_amount;
+				DEBUG_LOG_FIN(name,
+							  "Lower %s pref (%d in stock) %d -> %d",
+							  weapon[i].getName(),
+							  ROUND(cur_amount), currPref[i], ROUND(pref))
+
+				if (env.sellpercent > 0.01) {
+
+					// saleable are the units considered to be sold:
+					int32_t saleable = (div_amount - RAND_AI_1P) / one_amount;
+
+					if (saleable > 0) {
+						money += ROUNDu(weapon[i].cost * env.sellpercent)
+							   * saleable;
+						nm[i] -= weapon[i].amt * saleable;
+						DEBUG_LOG_FIN(name,
+									  "Sold %d %s for $%s",
+									  saleable,
+									  weapon[i].getName(),
+									  Add_Comma(ROUNDu( weapon[i].cost
+													  * env.sellpercent)
+													  * saleable))
+					}
+				} // end of selling allowed
+			} // end of having enough in stock
+		} // end of watching weapons
+
+		// Lower item preferences if there are enough in stock already
+		if (i >= WEAPONS) {
+			int32_t j = i - WEAPONS;
+			if ( (j < ITEM_ARMOUR) || (j > ITEM_VIOLENT_FORCE) ) {
+				double cur_amount = ni[j];
+				double one_amount = item[j].amt;
+				double max_amount = one_amount * ai_level;
+
+				// Note: The values are the same as above.
+
+				// Repair kit and SDI are limited differently
+				if (ITEM_REPAIRKIT == j)
+					max_amount *= painSensitivity + defensive + 2.;
+				else if (ITEM_SDI == j)
+					max_amount *= defensive + 2. + (ai_level / 2.);
+
+				double div_amount = cur_amount - max_amount;
+
+				if (div_amount >= 1.) {
+					pref /= div_amount;
+					DEBUG_LOG_FIN(name,
+								  "Lower %s pref (%d in stock) %d -> %d",
+								  item[j].getName(),
+								  ROUND(cur_amount), currPref[i],
+								  ROUND(pref))
+
+					if ( (env.sellpercent > 0.01) && (j < ITEM_VENGEANCE) ) {
+
+						// Note: Armour and Amps are not considered here!
+						int32_t saleable = (div_amount - RAND_AI_1P) / one_amount;
+
+						if (saleable > 0) {
+							money += ROUNDu(item[j].cost * env.sellpercent)
+								   * saleable;
+							ni[j] -= item[j].amt * saleable;
+							DEBUG_LOG_FIN(name,
+										  "Sold %d %s for $%s",
+										  saleable,
+										  item[j].getName(),
+										  Add_Comma(ROUNDu( item[j].cost
+														  * env.sellpercent)
+														  * saleable))
+						}
+					} // end of selling allowed
+				} // End of having enough in stock
+			} // End of item type limitation
+		} // End of being in items range
+
+		// Write back preferences:
+		currPref[i] = ROUND(pref);
+	}
+}
 
-  // we have no weapons, use default
-  if ( num_weapons == 0 )
-    return 0;
 
-  // pick a random offset from the bottom of the list
-  random_weapon = (rand() % num_weapons) + 1;
+/** @brief Buy item with index @a itemindex
+  * An item has been selected, this function merely buys it. It
+  * first does checks to make sure the item can be bought.
+  * The function returns true if we successfully bought the item or
+  * false if we could not get it for some reason.
+**/
+bool PLAYER::buy_item(int32_t itemindex, int32_t max_boost)
+{
+	bool bought  = true;
+
+	if (itemindex < WEAPONS) {
+		// The three things to test:
+		// 1: Enough money?
+		// 2: Space free in stock?
+		// 3: Tech level not too high?
+		if ( (money >= weapon[itemindex].cost)
+		  && (nm[itemindex] < MAX_ITEMS_IN_STOCK)
+		  && (weapon[itemindex].techLevel <= env.weapontechLevel) ) {
+			money         -= weapon[itemindex].cost;
+			nm[itemindex] += weapon[itemindex].amt;
+
+			// don't allow more than MAX_ITEMS_IN_STOCK
+			if (nm[itemindex] > MAX_ITEMS_IN_STOCK)
+				nm[itemindex] = MAX_ITEMS_IN_STOCK;
+		} else
+			bought = false;
+	} // end of buying a weapon
+
+	else {
+
+		// Items need an additional check:
+		// The purchase of boost items is limited by
+		// both AI type and overall boost level.
+		// The same applies to shields
+
+		int32_t ai_level = static_cast<int32_t>(type);
+		int32_t itemNum  = itemindex - WEAPONS;
+		bool    isBoost  = ( (itemNum >= ITEM_ARMOUR)
+		                  && (itemNum <= ITEM_VIOLENT_FORCE) );
+		bool    isShield = ( (itemNum >= ITEM_LGT_SHIELD)
+		                  && (itemNum <= ITEM_HVY_REPULSOR_SHIELD) );
+
+		if ( (money > item[itemNum].cost)
+		  && (ni[itemNum] < MAX_ITEMS_IN_STOCK)
+		  && env.isItemAvailable(itemNum)
+		  && ( (HUMAN_PLAYER == type )
+			|| !(isBoost || isShield)
+			|| ( isBoost
+			  && (ai_level > boostBought)
+			  && (getBoostValue() < max_boost) )
+			|| ( isShield
+			  && (ai_level > shieldBought) ) ) ) {
+			money -= item[itemNum].cost;
+			ni[itemNum] += item[itemNum].amt;
+
+			// Count it if it was a boost item
+			if (isBoost)
+				boostBought++; // Okay, take it!
+
+			// Count it if it was a shield
+			if (isShield)
+				shieldBought++; // Okay, same procedure
+
+			// don't allow more than MAX_ITEMS_IN_STOCK
+			if (ni[itemNum] > MAX_ITEMS_IN_STOCK)
+				ni[itemNum] = MAX_ITEMS_IN_STOCK;
+		} else
+			bought = false;
+	}
+
+	return bought;
+}
 
-  index = WEAPONS - 1;
-  while ( (index) && (random_weapon) )
-    {
-      if ( nm[index] )
-        random_weapon--;
-      if (random_weapon)
-        index--;
-    }
 
-  // If the (char *)"weapon" is a dirtball, skip it 75% of the time:
-  if	( ( (index	==	DIRT_BALL) || (index	==	LRG_DIRT_BALL)) && (rand() % 4))
-    return 0;
-
-  // Skip if it is an unburying tool
-  if	( (index	==	RIOT_CHARGE)
-       || (index	==	RIOT_BOMB)
-       || (index	==	RIOT_BLAST)
-       || (index	==	HVY_RIOT_BOMB))
-    return 0;
-  return index;
+/// @brief call this after loading a game to ensure backwards compatibility
+void PLAYER::checkOppMem()
+{
+	if (!oppCount)
+		newGame();
 }
 
 
+/// @brief Have the AI choosing something to buy.
+int32_t PLAYER::chooseItemToBuy (int32_t max_boost, int32_t &last_idx)
+{
+
+	// Do not do this if there is no money:
+	if (money < 1000)
+		return -1;
+
+	// Possibly pre-select an item by checking the current situation:
+	int32_t currItem = computerSelectPreBuyItem (max_boost);
+
+	// Be done already if the pre-selection provided a "must have"
+	if ( (currItem > 0) && buy_item(currItem, max_boost) )
+		return currItem;
+
+	// Loop through the wish list and try to buy something.
+	// The more of the item is in the inventory, the less likely
+	// it is that the AI tries to buy the item. If the inventory
+	// is empty, the chance is 50% for a useless bot and 88% for
+	// a deadly bot.
+	// There do not need to be any further modifications, the
+	// preferences are already tweaked by defensiveness and
+	// AI level.
+	int32_t ai_level = static_cast<int32_t>(type);
+	for ( ; last_idx < THINGS ; ++last_idx) {
+
+		currItem = desired[last_idx];
+
+		// Skip small missile, restart instead
+		if (!currItem) {
+			last_idx = 0;
+			// Note: Small missiles are sorted to the end of the list.
+			continue;
+		}
+
+		// Skip unaffordable items
+		if ( ( (currItem <  WEAPONS)
+		    && (weapon[currItem].cost > money) )
+		  || ( (currItem >= WEAPONS)
+		    && (item[currItem - WEAPONS].cost > money) ) )
+			continue;
+
+		// Now take the chance
+		int32_t amount  = currItem < WEAPONS
+		                ? weapon[currItem].amt
+		                : item[currItem - WEAPONS].amt;
+		int32_t maxAmt  = amount * ai_level;
+		double  currAmt = currItem < WEAPONS
+		                ? nm[currItem]
+		                : ni[currItem - WEAPONS];
+		double  newAmt  = currAmt + amount;
+		double  amtMod  = currAmt > 0. ? newAmt / currAmt : 2.;
+		// Note: The more items there are already, the more amtMod
+		// will go down near 1.0, from a maximum of 2.0.
+
+		int32_t chance = ROUNDu(amtMod * (static_cast<double>(ai_level) - .5));
+		/* Results:
+		 * Useless : 1 * (1 - 0.5) = 1 * (0.5) = 0.5 => 50% (rounded to 1)
+		 * Useless : 2 * (1 - 0.5) = 2 * (0.5) = 1   => 50%
+		 * Deadly  : 1 * (5 - 0.5) = 1 * (4.5) = 4.5 => 80% (rounded to 5)
+		 * Deadly  : 2 * (5 - 0.5) = 2 * (4.5) = 9   => 87.5%
+		 */
+
+		// Bots have far higher limits for repair units and SDIs:
+		if ( (ITEM_SDI       == (currItem - WEAPONS))
+		  || (ITEM_REPAIRKIT == (currItem - WEAPONS)) )
+			maxAmt *= 10;
+
+		/* The bot tries to buy the item if:
+		 * a) It does not have any and the chance is taken, or
+		 * b) It has equal or more than the weapons amount times its level and
+		 *    a negative (low) chance against its ai_level is taken.
+		 */
+		if ( ( ( (currAmt <  maxAmt) && (rand() % (chance + 1)) ) /* Scenario a) */
+		    || ( (currAmt >= maxAmt) && RAND_AI_0N ) )            /* Scenario b) */
+			 && buy_item(currItem, max_boost) ) {
+
+			// Advance index to not buy the same item over and over again
+			if (RAND_AI_1P)
+				++last_idx;
+
+			return currItem;
+		}
+	}
+
+	return -1;
+}
+
 
-// This function selects a random item for
-// the AI to use.
-// This item to use is returned. If no
-// item can be found, then the function
-// returns 0
-int PLAYER::Select_Random_Item()
+eControl PLAYER::computerControls (AICore* aicore, bool allow_fire)
 {
-  int index, item_num;
-  int num_items = 0;
-  int random_item;
+	// Don't act at all when in scoreboard or endgame stage
+	if (STAGE_SCOREBOARD <= global.stage)
+		return CONTROL_NONE;
+
+	int32_t       ai_weap    = tank ? tank->cw : SML_MIS;
+	int32_t       ai_angle   = 0;
+	int32_t       ai_power   = 0;
+	ePlayerStages ai_stage   = PS_STAGE_COUNT;
+	bool          is_working = aicore->status(ai_weap, ai_angle, ai_power, ai_stage);
+
+	// If the AI is working with a different player or the AI is dead, return
+    if ( !aicore->can_work()
+	  || (is_working && (this != aicore->active_player())) )
+		return CONTROL_NONE;
+
+	// Do not try to start the AI if this players tank is
+	// about to be destroyed or while it is moving.
+	if (!tank || tank->destroy || tank->isFlying() || (tank->l < 1) )
+		return CONTROL_NONE;
+
+	tank->requireUpdate ();
+
+	/* Start the AI for this player if:
+	 * 1) The AI is idle and
+	 * 2) the game is in aiming stage and
+	 * 3) this player still has a tank that (checked above)
+	 * 4) is not about to get destroyed and (checked above)
+	 * 5) stands still on the ground.       (checked above)
+	 */
+	if ( (PS_AI_IS_IDLE == ai_stage)
+	  && (STAGE_AIM     == global.stage) ) {
+
+		if (aicore->start(this))
+			return CONTROL_NONE;
+		else {
+			cerr << "FATAL: Can not start idle AI with this player!" << endl;
+			global.set_command(GLOBAL_COMMAND_MENU);
+			return CONTROL_QUIT;
+		}
+	}
+
+	// Return at once if the AI is still initializing or cleaning up
+	else if ( (PS_AI_INITIALIZE == ai_stage)
+	       || (PS_CLEANUP       == ai_stage) )
+		return CONTROL_NONE;
+
+	// If the AI is not working (yet), return
+	if (!is_working)
+		return CONTROL_NONE;
+
+	// Now, being here, the ai is working on this very player.
+
+	// Copy stage now, as it is this players stage as well
+	plStage = ai_stage;
+
+	// Sanitize AI values:
+	// Note: None of these should ever kick in!
+	assert( (ai_angle >=        90) && "ERROR: AI set too low angle!");
+	assert( (ai_angle <=       270) && "ERROR: AI set too high angle!");
+	assert( (ai_power >=         0) && "ERROR: AI set too low power!");
+	assert( (ai_power <= MAX_POWER) && "ERROR: AI set too high power!");
+	assert( (0 == (ai_power % 5)  ) && "ERROR: AI set non mod 5 power!");
+	assert( (   (ai_weap < 0) /* unset ! */
+	       || ( (ai_weap <  WEAPONS) && nm[ai_weap          ] > 0)
+	       || ( (ai_weap >= WEAPONS) && ni[ai_weap - WEAPONS] > 0) )
+	     && "ERROR: AI set weapon that has a zero stock!" );
+	if (ai_angle <        90) ai_angle = 90;
+	if (ai_angle >       270) ai_angle = 270;
+	if (ai_power <         0) ai_power = 0;
+	if (ai_power > MAX_POWER) ai_power = MAX_POWER;
+	ai_power -= ai_power % 5;
+	if ( ( (ai_weap <  WEAPONS) && nm[ai_weap          ] <= 0)
+	  || ( (ai_weap >= WEAPONS) && ni[ai_weap - WEAPONS] <= 0) )
+		ai_weap = tank->cw;
+
+	// Only put anything on the screen if this is the firing stage
+	if (PS_FIRE == plStage) {
+
+		// If there is a difference between the AI selection and the
+		// display, transport the values on the screen.
+		if ( (ai_angle != tank->a)
+		  || (ai_power != tank->p)
+		  || (ai_weap  != tank->cw) ) {
+			global.updateMenu = true;
+
+			if (global.skippingComputerPlay) {
+				// When skipping, the values are simply copied:
+				tank->a  = ai_angle;
+				tank->p  = ai_power;
+				tank->cw = ai_weap;
+			} else {
+				// Transfer angle:
+				if (ai_angle > tank->a)
+					++tank->a;
+				else if (ai_angle < tank->a)
+					--tank->a;
+
+				// Transfer power:
+				if (ai_power > tank->p)
+					tank->p += 5;
+				else if (ai_power < tank->p)
+					tank->p -= 5;
+
+				// Transfer weapon information:
+				if (ai_weap != tank->cw) {
+					changed_weapon = false;
+
+					int32_t cw_mod = tank->cw < ai_weap ? 1 : -1;
+					tank->cw += cw_mod;
+
+					// Skip unusable items and those that are
+					// out of stock
+					while ( ( !env.isItemAvailable(tank->cw)
+					       || ( (tank->cw <  WEAPONS) && !nm[tank->cw] )
+					       || ( (tank->cw >= WEAPONS) && !ni[tank->cw - WEAPONS] ) )
+					     && (tank->cw != ai_weap) )
+						tank->cw += cw_mod;
+				}
+			} // End of regular transfer
+		} // End of transferring difference
+
+		// otherwise fire the current weapon if this is allowed
+		else if (allow_fire) {
+			aicore->weapon_fired();
+			global.updateMenu = 1;
+
+			if (type == VERY_PART_TIME_BOT)
+				type = NETWORK_CLIENT;
+
+			gloating = false;
+			plStage  = PS_CLEANUP;
+			return CONTROL_FIRE;
+		}
+	} // End of handling firing stage
+
+	return CONTROL_NONE;
+}
 
-  // count the items we have
 
-  for (index = WEAPONS; index < THINGS; index++)
-    {
-      item_num = index - WEAPONS;
+int32_t PLAYER::computerSelectPreBuyItem (int32_t max_boost)
+{
+	int32_t max_level = static_cast<int32_t>(DEADLY_PLAYER);
+	int32_t ai_level  = static_cast<int32_t>(type);
+	double  mood      = 1. + defensive + (
+	                    (static_cast<double>(rand())
+	                  / (static_cast<double>(RAND_MAX) / 2.)) );
+	// mood is 0.0 <= x <= 4.0
+
+	/*	Prior buying anything else, a 5 step system takes place:
+	 * 1.: Parachutes (if gravity is on)
+	 * 2.: Minimum weapon probability (aka 5 medium and 3 large missiles)
+	 * 3.: The most expensive item from the saveMoneyFor list
+	 * 4.: Armour/Amps
+	 * 5.: "Tools" to free themselves like Riot Blasts
+	 * 6.: Shields, if enough money is there
+	 * 7.: if all is set, look for dimpled/slick projectiles!
+	 */
+
+
+	// Step 1: Check for parachutes (if the bot remembers to check)
+	if ( ( (type >= RANGEFINDER_PLAYER)
+		|| RAND_AI_1P )
+	  && (env.landSlideType > SLIDE_NONE)
+	  && (ni[ITEM_PARACHUTE] < 10)
+	  && (money > item[ITEM_PARACHUTE].cost) ) {
+
+		DEBUG_LOG_FIN(name, "Pre-selecting Parachute", 0)
+		return (WEAPONS + ITEM_PARACHUTE);
+	}
+
+
+    // Step 2: Minimum range of damaging weapons:
+    // To be fair, this is always done and never forgotten.
+	if ( (nm[LRG_MIS] < 3) && (money >= weapon[LRG_MIS].cost) ) {
+
+		DEBUG_LOG_FIN(name, "Pre-selecting Large Missile", 0)
+		return LRG_MIS;
+	}
+
+	if ( (nm[MED_MIS] < 5) && (money >= weapon[MED_MIS].cost) ) {
+
+		DEBUG_LOG_FIN(name, "Pre-selecting Medium Missile", 0)
+		return MED_MIS;
+	}
+
+
+	// Step 3: Check weapons/items currently saving money for:
+	int32_t saved_cost = 0;
+	int32_t saved_item = 0;
+	for (int32_t i = 0; i < THINGS; ++i) {
+		if ( (saveMoneyFor[i] > 0)
+		  && (saveMoneyFor[i] < money)
+		  && (saveMoneyFor[i] > saved_cost) ) {
+			saved_cost = saveMoneyFor[i];
+			saved_item = i;
+		}
+	}
+	// Got one?
+	if (saved_item > 0) {
+		DEBUG_LOG_FIN(name, "Finally got enough money for %s!",
+		              saved_item < WEAPONS
+		                ? weapon[saved_item].getName()
+		                : item[saved_item - WEAPONS].getName())
+		// Take it out from the wish list:
+		saveMoneyFor[saved_item] = 0;
+		return saved_item;
+	}
+
+
+	// Step 4: Check for Armour / Amps (if the bot remembers to check)
+	if ( (boostBought < ai_level) && RAND_AI_1P ) {
+		int32_t boost_value = getBoostValue();
+		int32_t boost_limit = max_boost - boost_value;
+		int32_t armour_val  = getArmourValue();
+		int32_t amp_val     = getAmpValue();
+
+
+		if ( (boost_limit > (max_level - ai_level + 1))
+		  && (!needDamage || RAND_AI_0P) ) {
+
+			DEBUG_LOG_FIN(name,
+						"Pre-Check: Max Boost %d, Armour %d, Amp %d, Limit %d",
+						max_boost, armour_val, amp_val, boost_limit);
+
+			// See which is preferred:
+			boost_limit = max_boost - (2 * (DEADLY_PLAYER + 1)) + RAND_AI_0P;
+
+			if ( boost_value < boost_limit) {
+				if ( (amp_val    - defensive)
+				   < (armour_val + defensive) ) {
+					needAmp    = true;
+					needArmour = false;
+				} else {
+					needAmp    = false;
+					needArmour = true;
+				}
+			}
+
+
+			// Prefer armour if the mood is defensive and armour isn't too
+			// far ahead from the amps:
+			if ( needArmour || ( (mood >= 2.0)  && (armour_val <= amp_val) ) ) {
+				// The player is in a defensive mood or armour has fallen behind
+				// If we have 25% more money than the plasteel cost, buy it,
+				// else the armour will do. If the armour is far behind, no
+				// money is spared.
+				if ( (money >= (item[ITEM_PLASTEEL].cost * 1.25) )
+				  || ( ( (armour_val < (amp_val * 0.5)) || !armour_val)
+					&& (money > item[ITEM_PLASTEEL].cost) ) ) {
+
+					DEBUG_LOG_FIN(name, "Pre-selecting Plasteel Plating", 0)
+					return (WEAPONS + ITEM_PLASTEEL);
+
+				}
+
+				if ( (money >= (item[ITEM_ARMOUR].cost * 2.0))
+				  && (ni[ITEM_ARMOUR] < ni[ITEM_PLASTEEL])
+				  && (mood >= 3.5) ) {
+
+					DEBUG_LOG_FIN(name, "Pre-selecting Armour", 0)
+					return (WEAPONS + ITEM_ARMOUR);
+				}
+			} // End of armour check
+
+			// Otherwise go for a shining new amp:
+			if ( needAmp || ( (mood <= 2.0)  && (amp_val <= armour_val) ) ) {
+				// The player is in a offensive mood or amps have fallen behind
+				// If we have 25% more money than the violent force cost, buy
+				// it, else the normal amp will do.
+				// If the amps have fallen behind too much, no money is spared.
+				if ( (money >= (item[ITEM_VIOLENT_FORCE].cost * 1.5) )
+				  || ( ( (amp_val < (armour_val * 0.5) ) || !amp_val)
+					&& (money > item[ITEM_VIOLENT_FORCE].cost) ) ) {
+
+					DEBUG_LOG_FIN(name, "Pre-selecting Violent Force", 0)
+					return (WEAPONS + ITEM_VIOLENT_FORCE);
+
+				}
+
+				if ( (money >= (item[ITEM_INTENSITY_AMP].cost * 1.75))
+				  && (ni[ITEM_INTENSITY_AMP] < ni[ITEM_VIOLENT_FORCE])
+				  && (mood < 1.0) ) {
+
+					DEBUG_LOG_FIN(name, "Pre-selecting Intensity Amp", 0)
+					return (WEAPONS + ITEM_INTENSITY_AMP);
+				}
+			} // End of amp check
+		} // End of being allowed to by boost items
+	} // end of step 3
+
+
+	// 5.: Freeing tools
+	if (RAND_AI_0P) {
+		if ( !nm[HVY_RIOT_BOMB] || !nm[RIOT_BOMB] || (mood < 2.) ) {
+
+			// More offensive in this round, check for riot bombs
+			if ( (nm[HVY_RIOT_BOMB] < 2)
+			  && (money >= weapon[HVY_RIOT_BOMB].cost) ) {
+
+				DEBUG_LOG_FIN(name, "Pre-selecting Heavy Riot Bomb", 0)
+				return HVY_RIOT_BOMB;
+			}
+
+			if ( (nm[RIOT_BOMB] < 5)
+			  && (money >= weapon[RIOT_BOMB].cost) ) {
 
-      if ( (ni[item_num]) && (item[item_num].selectable))
-        num_items++;
-    }    // end of counting items
+				DEBUG_LOG_FIN(name, "Pre-selecting Riot Bomb", 0)
+				return RIOT_BOMB;
+			}
+		} else {
+			// In a defensive mood the charges are checked
+			if ( (nm[RIOT_BLAST] < 2)
+			  && (money >= weapon[RIOT_BLAST].cost) ) {
 
-  if (! num_items)
-    return 0;
+				DEBUG_LOG_FIN(name, "Pre-selecting Riot Blast", 0)
+				return RIOT_BLAST;
+			}
+
+			if ( (nm[RIOT_CHARGE] < 5)
+			  && (money >= weapon[RIOT_CHARGE].cost) ) {
+
+				DEBUG_LOG_FIN(name, "Pre-selecting Riot Charge", 0)
+				return RIOT_CHARGE;
+			}
+		}
+	} // End of step 4
+
+
+	// 6.: Shields
+	if ((shieldBought < ai_level) && RAND_AI_1P) {
+		if (mood <= 1.5) {
+
+			// offensive type, go through reflectors
+			if ( (   ni[ITEM_LGT_REPULSOR_SHIELD]
+			      <= (item[ITEM_LGT_REPULSOR_SHIELD].amt * ai_level) )
+			  && (money >= (item[ITEM_LGT_REPULSOR_SHIELD].cost * 2.0)) ) {
 
-  // if we have an item, there is still a 75% chance
-  // that we may not use it
-  if (rand() % 4)
-    return 0;
+				DEBUG_LOG_FIN(name, "Pre-selecting Light Repulsor Shield", 0)
+				return (WEAPONS + ITEM_LGT_REPULSOR_SHIELD);
+			}
 
-  // pick a random offset from the bottom of the list
-  random_item = (rand() % num_items) + 1;
+			if ( (   ni[ITEM_MED_REPULSOR_SHIELD]
+			      <= (item[ITEM_MED_REPULSOR_SHIELD].amt * ai_level) )
+			  && (money >= (item[ITEM_MED_REPULSOR_SHIELD].cost * 1.75)) ) {
 
-  index		= THINGS - 1;
+				DEBUG_LOG_FIN(name, "Pre-selecting Medium Repulsor Shield", 0)
+				return (WEAPONS + ITEM_MED_REPULSOR_SHIELD);
+			}
 
-  item_num =	index - WEAPONS;
+			if ( (   ni[ITEM_HVY_REPULSOR_SHIELD]
+			      <= (item[ITEM_HVY_REPULSOR_SHIELD].amt * ai_level) )
+			  && (money >= (item[ITEM_HVY_REPULSOR_SHIELD].cost * 1.5)) ) {
 
-  while (item_num && random_item)
-    {
-      if ( (ni[item_num]) && (item[item_num].selectable))
-        random_item--;
+				DEBUG_LOG_FIN(name, "Pre-selecting Heavy Repulsor Shield", 0)
+				return (WEAPONS + ITEM_HVY_REPULSOR_SHIELD);
+			}
+		} // End of offensive mood
 
-      if (random_item)
-        {
-          index--;
-          item_num	=	index - WEAPONS;
-        }
-    }
+		if (mood >= 2.5) {
 
-  if ((item_num	==	ITEM_TELEPORT) || (item_num == ITEM_SWAPPER))
-    index = 0;
+			// defensive type, go through hard shields
+			if ( (   ni[ITEM_LGT_SHIELD]
+			      <= (item[ITEM_LGT_SHIELD].amt * ai_level) )
+			  && (money >= (item[ITEM_LGT_SHIELD].cost * 2.0)) ) {
 
-//  // do not use teleport without a parachute
-//  if ( (item_num == ITEM_TELEPORT) && (! ni[ITEM_PARACHUTE]))
-//    index = 0;
+				DEBUG_LOG_FIN(name, "Pre-selecting Light Shield", 0)
+				return (WEAPONS + ITEM_LGT_SHIELD);
+			}
 
-  // do not self destruct
-  if ( (item_num	==	ITEM_VENGEANCE)
-       || (item_num	==	ITEM_DYING_WRATH)
-       || (item_num	==	ITEM_FATAL_FURY))
-    index = 0;
+			if ( (   ni[ITEM_MED_SHIELD]
+			      <= (item[ITEM_MED_SHIELD].amt * ai_level) )
+			  && (money >= (item[ITEM_MED_SHIELD].cost * 1.75)) ) {
 
-  if (index > 0)
-    index = item_num + WEAPONS;
-  return index;
-}
+				DEBUG_LOG_FIN(name, "Pre-selecting Medium Shield", 0)
+				return (WEAPONS + ITEM_MED_SHIELD);
+			}
 
-// This function takes one off the player's time to fire.
-// If the player runs out of time, the function returns TRUE.
-// If the player has time left, or no time clock is being used,
-// then the function returns FALSE.
-int PLAYER::Reduce_Time_Clock()
-{
-  if (! time_left_to_fire)     // not using clock
-    return FALSE;
-
-  time_left_to_fire--;
-  if (! time_left_to_fire)    // ran out of time
-    {
-      tank->shots_fired++;      // pretend we fired
-      time_left_to_fire = _global->max_fire_time;
-      return TRUE;
-    }
-  return FALSE;
-}
+			if ( (   ni[ITEM_HVY_SHIELD]
+			      <= (item[ITEM_HVY_SHIELD].amt * ai_level) )
+			  && (money >= (item[ITEM_HVY_SHIELD].cost * 1.5)) ) {
 
-// The damage provided is not the weapon damage, but what the bot calculated!
-int PLAYER::getBlastValue (TANK * aTarget, int aDamage, int aWeapon, double aDamageMod)
-{
-  int iHealth				= 0;	//	Health of the evaluated tank
-  int iTeam;							//	Team of the evaluated tank
-  double dDistance;				//	Distance of the target to the evaluated tank
-  double dXdist, dYdist;	//	needed to calculate dDistance
-  int iDmgValue			=	0;	//	The value of the blast
-  int iBlastDamage	= 0;	//	The damage the weapon is doing right there
-  double dTeamMod 	= 1.0;//	Teams react differently on TAs
-  int iWeapon				=	0;	//	Items (self destruc) count as CUTTERS for their blast radius!
-  TANK * cOppTank;
-
-  if (aWeapon < WEAPONS)
-    iWeapon = aWeapon;
-  else
-    iWeapon	=	CUTTER;
-
-  for (int i = 0; i < _global->numPlayers; i++)
-    {
-      cOppTank	=	_global->players[i]->tank;
-      if (cOppTank && aTarget)
-        {
-          if (this == _global->players[i])
-            // If we'd hit ourself, life is valued twice.
-            iHealth = (cOppTank->l / 2) + cOppTank->sh;
-          else
-            iHealth = cOppTank->l + cOppTank->sh;
-          iTeam		=	_global->players[i]->team;
-          // Only count living targets that are not the official target
-          if ( (iHealth > 0) && (cOppTank != aTarget))
-            {
-              dXdist	=	fabs (aTarget->x - cOppTank->x) - (TANKWIDTH / 2);
-              dYdist	=	fabs (aTarget->y - cOppTank->y) - (TANKHEIGHT / 2);
-              if (dXdist < 0) dXdist = 1.0;
-              if (dYdist < 0) dYdist = 0.0;
-              if	( (aWeapon >= SHAPED_CHARGE)
-                   && (aWeapon <= CUTTER)
-                   && ( (dYdist		>=	(weapon[iWeapon].radius / 20))
-                        || (dXdist		<=	(TANKWIDTH / 2))))
-                dDistance = 2.0 * (double) weapon[iWeapon].radius; // out of the way!
-              else
-                dDistance = ABSDISTANCE(dXdist, dYdist, 0, 0) - ( (double) type * (defensive + 2.0));
-
-              // Now see whether the tank is in weapon blast range:
-              if (dDistance <= weapon[iWeapon].radius)
-                {
-                  // Yep, it is!
-                  iBlastDamage = (int) ( (double) aDamage * (1.0 - ( (dDistance / (double) weapon[iWeapon].radius) / 2.0)));
-                  if	( ( (team != TEAM_NEUTRAL) && (team == iTeam))
-                       || (this == _global->players[i]))
-                    {
-                      // it is a fellow team mate, or ourself
-                      // teams act differently
-                      switch ( (int) team)
-                        {
-                        case TEAM_JEDI:
-                          dTeamMod	= ( (double) type	/ 2.0) + 1.5;		// 2.0 up to 4.0
-                          break;
-                        case TEAM_SITH:
-                          dTeamMod	= ( (double) type / 4.0) + 0.75;	// 1.0 up to 2.0
-                          break;
-                        default:
-                          dTeamMod	=	(double) type;	// neutrals won't like to hit themselves at all!
-                        }
-
-                      if (iBlastDamage > iHealth)
-                        // We would kill our mate, or cost us too much health!
-                        iDmgValue -= (int) (dTeamMod	* ( (double) aDamage 			/ aDamageMod)	* ( (defensive + 3.0) / 2.0));
-                      else
-                        // We don't kill, but count the blast at least
-                        iDmgValue -= (int) (dTeamMod	* ( (double) iBlastDamage / aDamageMod)	* ( (defensive + 3.0) / 2.0));
-
-                      // Note: The /=aDamageMod results in blast damage counted worse, if the aDamageMod is low
-                    }
-                  else
-                    {
-                      // Just Someone... see if we kill them
-                      if (iBlastDamage > iHealth)
-                        // Woohoo!
-                        iDmgValue	+=	(int) ( ( (1.0 + (double) type / 10))
-                                             * (double) (iBlastDamage + iHealth)
-                                             * ( (defensive + 3.0) / 2.0));
-                      else
-                        // Well, at least...
-                        iDmgValue	+=	(int) ( ( (1.0 + (double) type / 10))
-                                             * aDamageMod
-                                             * (double) (iBlastDamage)
-                                             * ( (defensive - 3.0) / -2.0));
-                    }
-                }
-            }
-        }
-    }
-#ifdef DEBUG
-  if ((iDmgValue != 0) && (aDamageMod != 1.0))
-    cout << "(blast: " << iDmgValue << ") ";
-#endif
-  return (iDmgValue);
-}
+				DEBUG_LOG_FIN(name, "Pre-selecting Heavy Shield", 0)
+				return (WEAPONS + ITEM_HVY_SHIELD);
+			}
+		} // End of defensive mood
+	} // End of step 5
 
-int PLAYER::getMoneyToSave()
-{
-  double dMoneyToSave	= 0.0;
-  int iAvgPref			=	0;
-  int iPrefLimit    = 0;
-  int iPrefCount    = 0;
-  int iInvCount     = 0;
-  int iInvMoney     = 0;
-  int iMaxCost			=	0;
-
-  // if the preferences are exceptionally low, a div by 0 might occur, so it has to be dynamized:
-  for (int i = 0; i < WEAPONS; i++)
-    {
-      if (_weaponPreference[i] > iPrefLimit)
-        iPrefLimit = (iPrefLimit + _weaponPreference[i]) / 2;
-    }
-  // Now it is guaranteed, that iPrefCount and iAvgPref will result in values > 0!
-  for (int i = 0; i < WEAPONS; i++)
-    {
-      if (_weaponPreference[i] > iPrefLimit)
-        {
-          iPrefCount++;
-          iAvgPref += _weaponPreference[i];
-        }
-    }
-  // we are running into divide by zero here.
-  if (iPrefCount < 1) 
-      iPrefCount = 1;
-  iAvgPref /= iPrefCount; // Now the average of all relevant weapon preferences
-
-  // Now we search for everything over this average and count costs and inventory value:
-  for (int i = 0; i < WEAPONS; i++)
-    {
-      if (_weaponPreference[i] > iAvgPref)
-        {
-          // This weapon is (char *)"wanted"
-          dMoneyToSave += weapon[i].cost;
-          if (weapon[i].cost > iMaxCost)
-            iMaxCost	=	weapon[i].cost;
-          if (nm[i])
-            {
-              // We have (some) if this already!
-              iInvCount++;
-              iInvMoney += (int) ( ( (double) nm[i] / (double) weapon[i].amt) * (double) weapon[i].cost);
-            }
-        }
-    }
 
-  // The Maximum amount is (type * 2) times the most expensive weapon we like:
-  iMaxCost *= (int) type * 2;
-  // As of writing this, this means a maximum of:
-  // Armageddon: 100,000 credits
-  // Deadly Bot: (int)type == 5
-  // iMaxCost = 100,000 * 5 * 2 = 1,000,000 credits maximum!
-
-     dMoneyToSave = (double)iMaxCost * 0.25;
-#ifdef DEBUG
-  printf( (char *)"% 4d Prefs, worth % 6d\n", iPrefCount, iAvgPref);
-  printf( (char *)"% 4d Items, worth % 6d\n", iInvCount, iInvMoney);
-  printf( (char *)"MoneyToSave:      % 6d\n", (int)dMoneyToSave);
-  printf( (char *)"Money: % 6d -> MaxCost: %6d\n", (int)money, iMaxCost);
-#endif // DEBUG
-  // Whenever dMoneyToSave is less than the money owned, iBoostItemsBought is resetted
-  if (money > (int)dMoneyToSave)
-    iBoostItemsBought = 0; // Let's go!
-  return ( (int) dMoneyToSave);
-}
+    // Step 7: Slick / Dimpled Projectiles
+    if ( (ni[ITEM_SLICKP] + ni[ITEM_DIMPLEP]) < 100 ) {
 
+		if ( (ni[ITEM_DIMPLEP] < 50)
+		  && (money >= item[ITEM_DIMPLEP].cost) ) {
 
+			DEBUG_LOG_FIN(name, "Pre-selecting Dimpled Projectiles", 0)
+			return (WEAPONS + ITEM_DIMPLEP);
+		}
 
-// if we have some shield strength at the end of the round, then
-// reclaim this shield back into our inventory
+		if ( (ni[ITEM_SLICKP] < 50)
+		  && (money >= item[ITEM_SLICKP].cost) ) {
 
-int PLAYER::Reclaim_Shield()
-{
-    if (! tank)
-       return FALSE; 
-    if (! last_shield_used)
-       return FALSE;
-
-    if (tank->sh > 0)
-       ni[last_shield_used] += 1;
-   
-    last_shield_used = 0;
-    return TRUE;
-}
+			DEBUG_LOG_FIN(name, "Pre-selecting Slick Projectiles", 0)
+			return (WEAPONS + ITEM_SLICKP);
+		}
+    }
 
+	return -1;
+}
 
 
+eControl PLAYER::controlTank (AICore* aicore, bool allow_fire)
+{
+	// Handle User input, this is read for providing the ingame menu
+	// even when no human player is active. Otherwise the player would
+	// not be able to enter the ingame menu whenever an AI player is
+	// active.
+	k = 0;
+	K = 0;
+	if (key_shifts & KB_CTRL_FLAG)  has_ctrl_pressed  = true;
+	else                            has_ctrl_pressed  = false;
+	if (key_shifts & KB_SHIFT_FLAG) has_shift_pressed = true;
+	else                            has_shift_pressed = false;
+	if (keypressed() ) {
+		k = readkey ();
+		K = k >> 8;
+
+		// Enter ingame menu?
+		if ( (KEY_ESC == K) || (KEY_P == K) ) {
+			int32_t mm = env.ingamemenu ();
+
+			global.make_update (0, 0, env.screenWidth, env.screenHeight);
+			global.make_bgupdate (0, 0, env.screenWidth, env.screenHeight);
+
+			switch (mm) {
+				case 1:
+					// Main Menu
+					global.set_command(GLOBAL_COMMAND_MENU);
+					return CONTROL_QUIT;
+				case 2:
+					// Quit the game
+					global.set_command(GLOBAL_COMMAND_QUIT);
+					return CONTROL_QUIT;
+				case 3:
+					// Skip AI
+					if (STAGE_SCOREBOARD > global.stage)
+						return CONTROL_SKIP;
+				default:
+					break;
+			}
+		}
+
+		// check for number key being pressed
+		if ( (K >= KEY_0) && (K <= KEY_9) ) {
+			int32_t value = K - KEY_0;
+			K = 0;
+
+			// make sure the value is within range
+			if ( (value < env.numGamePlayers)
+			  && env.players[value] ) {
+
+				TANK *my_tank = env.players[value]->tank;
+
+				if (my_tank) {
+					snprintf(global.tank_status, 127,
+							 "%s: %d + %d -- Team: %s",
+							 env.players[value]->name,
+							 my_tank->l,
+							 my_tank->sh,
+							 env.players[value]->getTeamName() );
+					global.tank_status_colour = env.players[value]->color;
+					global.updateMenu = 1;
+				} else
+					memset(global.tank_status, 0, sizeof(char) * 128);
+			}
+		} // end of check status keys
+
+		// Check for scorboard toggle key
+		if ( (K == KEY_TILDE) || (K == KEY_SLASH) ) {
+			K = 0;
+			global.showScoreBoard = !global.showScoreBoard;
+			if (!global.showScoreBoard) {
+				// erase it:
+				global.make_update  (0, MENUHEIGHT, 300,
+							 (env.maxNumTanks + 1) * env.fontHeight );
+				global.make_bgupdate(0, MENUHEIGHT, 300,
+							 (env.maxNumTanks + 1) * env.fontHeight );
+			}
+		}
+
+		// Handle volume control
+		if (KEY_V == K) {
+			K = 0;
+			if (has_shift_pressed)
+				env.increaseVolume();
+			else
+				env.decreaseVolume();
+		}
+	} // End of handling all time possible key presses.
+
+
+	if (key[KEY_F1]) {
+		int32_t nr = 0;
+		do {
+			snprintf(path_buf, PATH_MAX, "screenshot_%03d.bmp", ++nr);
+		} while (!access(path_buf, F_OK));
+
+		if (nr < 1000)
+			save_bmp(path_buf, global.canvas, nullptr);
+	}
+
+
+	if (has_ctrl_pressed && ctrlUsedUp) {
+		if ( !(key[KEY_LEFT]
+			|| key[KEY_RIGHT]
+			|| key[KEY_UP]
+			|| key[KEY_DOWN]
+			|| key[KEY_PGUP]
+			|| key[KEY_PGDN]
+			|| key[KEY_A]
+			|| key[KEY_D]
+			//additional control
+			|| key[KEY_W]
+			|| key[KEY_S]
+			|| key[KEY_R]
+			|| key[KEY_F]) )
+			ctrlUsedUp = false;
+	} else
+		ctrlUsedUp = false;
+
+
+	// A) HUMAN
+	if ( (HUMAN_PLAYER == type) || !tank)
+		return humanControls (aicore);
+
+	// B) Network Client
 #ifdef NETWORK
+	else if (type == NETWORK_CLIENT)
+		return executeNetCmd(true, aicore);
+#endif // NETWORK
 
+	// C) AI Player
+	else if (global.stage == STAGE_AIM)
+		return computerControls(aicore, allow_fire);
 
-// Removes the newline character and anything after it
-// from a string.
-void PLAYER::Trim_Newline(char *line)
-{
-    int index = 0;
-
-    while ( line[index] )
-    {
-       if ( (line[index] == '\n') || (line[index] == '\r') )
-          line[index] = '\0';
-       else
-          index++;
-    }
+	return CONTROL_NONE;
 }
 
 
-// This function checks for incoming data from a client.
-// If data is coming in, we put the incoming data in the net_command
-// variable. If the socket connection is broken, then we will
-// close the socket and hand control over to the AI.
-int PLAYER::Get_Network_Command()
+void PLAYER::drawIndicator(int32_t x, int32_t y, int32_t h)
 {
-   int status;
-
-   status = Check_For_Incoming_Data(server_socket);
-   if (status)
-   {
-      // we have something coming down the pipe
-      memset(net_command, '\0', NET_COMMAND_SIZE);    // clear buffer
-      status = read(server_socket, net_command, NET_COMMAND_SIZE);
-      if (! status)   // connection is broken
-      {
-         close(server_socket);
-         type = DEADLY_PLAYER;
-         printf("%s lost network connection. Returning control to AI.\n", _name);
-         return FALSE;
-      }
-      else   // we got data
-      {
-         net_command[NET_COMMAND_SIZE - 1] = '\0';
-         Trim_Newline(net_command);
-      }
-   }
-   return TRUE;
+	if (HUMAN_PLAYER == type) {
+		int32_t radius = ROUND(static_cast<double>(h) / 2.) - 2;
+		circlefill (global.canvas, x + radius + 2, y + radius + 2, radius,
+						makecol (200, 100, 255));
+		circle     (global.canvas, x + radius + 2, y + radius + 2, radius,
+						BLACK);
+	} else {
+		rectfill (global.canvas, x, y + 2, x + 15, y + h - 1, BLACK);
+		for (int32_t i = 0; i < type; ++i)
+			rectfill(global.canvas,
+					x + (3 * i) + 1, y + 3,
+					x + (3 * i) + 2, y + h - 2,
+					makecol (100, 255, 100));
+	}
 }
 
 
+#ifdef NETWORK
 // This function gets called during a round when a networked
 // player gets to act. The function checks to see if anything
 // is in the net_command variable. If there is, it handles
@@ -4548,12 +967,12 @@ int PLAYER::Get_Network_Command()
 //
 // We should have some time keeping in here before this goes live
 // to avoid hanging the game.
-
-int PLAYER::Execute_Network_Command(int my_turn)
+eControl PLAYER::executeNetCmd(bool my_turn, AICore* aicore)
 {
    char buffer[NET_COMMAND_SIZE];
-   static int player_index = -1;
+   static int playerindex = -1;
    static int fire_delay = 0, net_delay = 0;
+	int32_t towrite, written;
 
    if (my_turn)
    {
@@ -4561,10 +980,9 @@ int PLAYER::Execute_Network_Command(int my_turn)
        // if enough time has passed, we give up and turn control over to the AI
        if (fire_delay >= NET_DELAY)
        {
-          setComputerValues();
           type = VERY_PART_TIME_BOT;
           fire_delay = 0;
-          return ( computerControls() );
+          return computerControls(aicore, true);
        }
    }
 
@@ -4579,27 +997,20 @@ int PLAYER::Execute_Network_Command(int my_turn)
               type = VERY_PART_TIME_BOT;
               strcpy(buffer, "PING");
               write(server_socket, buffer, strlen(buffer));
-              return (computerControls());
+              return computerControls();
            }
-           else*/ 
+           else*/
            if (net_delay >= NET_DELAY_SHORT)
-           {
                // prompt the client to respond
-               strcpy(buffer, "PING");
-               write(server_socket, buffer, strlen(buffer));
-               return 0;
-           }
-           return 0;
+				SAFE_WRITE(server_socket, "%s", "PING")
+           return CONTROL_NONE;
    }     // we did not get a command to process
 
    else
      net_delay = 0;       // we got something, so reset timer
 
    if (! strncmp(net_command, "VERSION", 7) )
-   {
-       sprintf(buffer, "SERVERVERSION %s", VERSION);
-       write(server_socket, buffer, strlen(buffer) );
-   }
+		SAFE_WRITE(server_socket, "SERVERVERSION %s", VERSION)
    else if (! strncmp(net_command, "CLOSE", 5) )
    {
        close(server_socket);
@@ -4608,63 +1019,53 @@ int PLAYER::Execute_Network_Command(int my_turn)
    else if (! strncmp(net_command, "BOXED", 5) )
    {
       char buffer[32];
-      if (_global->bIsBoxed)
-         strcpy(buffer, "BOXED 1");
-      else
-         strcpy(buffer, "BOXED 0");
-      write(server_socket, buffer, strlen(buffer));
+		SAFE_WRITE(server_socket, "BOXED %d", env.isBoxed ? 1 : 0);
    }
    else if (! strncmp(net_command, "GOSSIP", 6) )
    {
-      snprintf(_global->tank_status, 128, "%s", & (net_command[7]) );
-      _global->updateMenu = TRUE;
+      snprintf(global.tank_status, 127, "%s", & (net_command[7]) );
+      global.updateMenu = TRUE;
    }
    else if (! strncmp(net_command, "HEALTH", 6) )
    {
-       int tank_index;
+       int tankindex;
        char buffer[64];
 
-       sscanf( &(net_command[7]), "%d", &tank_index );
-       if ( (tank_index >= 0) && (tank_index < _global->numPlayers) )
+       sscanf( &(net_command[7]), "%d", &tankindex );
+       if ( (tankindex >= 0) && (tankindex < env.numGamePlayers) )
        {
-           if (_global->players[tank_index]->tank)
-           {
-               snprintf(buffer, 64, "HEALTH %d %d %d %d", tank_index,
-                        _global->players[tank_index]->tank->l,
-                        _global->players[tank_index]->tank->sh,
-                        _global->players[tank_index]->tank->sht);
-               write(server_socket, buffer, strlen(buffer));
-           }
+           if (env.players[tankindex]->tank)
+				SAFE_WRITE(server_socket, "HEALTH %d %d %d %d", tankindex,
+						env.players[tankindex]->tank->l,
+						env.players[tankindex]->tank->sh,
+						env.players[tankindex]->tank->sht)
        }
 
    }
    else if (! strncmp(net_command, "ITEM", 4) )
    {
        char buffer[32];
-       int item_index;
-       sscanf( &(net_command[5]), "%d", &item_index);
-       if ( (item_index >= 0) && (item_index < ITEMS) )
-       {
-          sprintf(buffer, "ITEM %d %d", item_index, ni[item_index]);
-          write(server_socket, buffer, strlen(buffer));
-       }
+       int itemindex;
+       sscanf( &(net_command[5]), "%d", &itemindex);
+       if ( (itemindex >= 0) && (itemindex < ITEMS) )
+			SAFE_WRITE(server_socket, "ITEM %d %d", itemindex, ni[itemindex])
    }
    else if (! strncmp(net_command, "MOVE", 4) )
    {
-       if (! my_turn) return 0;
+       if (! my_turn) return CONTROL_NONE;
        if (tank)
        {
            if (strstr(net_command, "LEFT") )
-              tank->Move_Tank(DIR_LEFT);
+              tank->moveTank(DIR_LEFT);
            else
-              tank->Move_Tank(DIR_RIGHT);
-           _global->updateMenu = 1;
+              tank->moveTank(DIR_RIGHT);
+           global.updateMenu = 1;
        }
    }
-   else if (! strncmp(net_command, "FIRE", 4) ) 
+   else if (! strncmp(net_command, "FIRE", 4) )
    {
        int angle = 180, power = 1000, item = 0;
-       if (! my_turn) return 0;
+       if (! my_turn) return CONTROL_NONE;
        sscanf( & (net_command[5]), "%d %d %d", &item, &angle, &power);
        fire_delay = 0;
        if (tank)
@@ -4687,7 +1088,6 @@ int PLAYER::Execute_Network_Command(int my_turn)
            tank->p = power;
            if (tank->p > 2000) tank->p = 2000;
            else if (tank->p < 0) tank->p = 0;
-           tank->simActivateCurrentSelection();
            gloating = false;
            net_command[0] = '\0';
            return CONTROL_FIRE;
@@ -4700,54 +1100,49 @@ int PLAYER::Execute_Network_Command(int my_turn)
        bool found = false;
        char buffer[128];
 
-       while ( (player_index < _global->numPlayers) && (! found) )
+       while ( (playerindex < env.numGamePlayers) && (! found) )
        {
-           if ( _global->players[player_index] == this )
+           if ( env.players[playerindex] == this )
            {
                found = true;
-               sprintf(buffer, "YOUARE %d", player_index);
+				SAFE_WRITE(server_socket, "YOUARE %d", playerindex)
            }
            else
-              player_index++;
+              playerindex++;
        }
        // check to see if something went very wrong
        if (! found)
-           strcpy(buffer, "YOUARE -1");
-       write(server_socket, buffer, strlen(buffer));
+			SAFE_WRITE(server_socket, "YOUARE %d", -1)
    }
    // return wind speed
    else if (! strncmp(net_command, "WIND", 4) )
    {
       char buffer[64];
-      sprintf(buffer, "WIND %f", _env->wind);
-      write(server_socket, buffer, strlen(buffer));
+		SAFE_WRITE(server_socket, "WIND %f", global.wind)
    }
 
    // find out how many players we have
    else if (! strncmp(net_command, "NUMPLAYERS", 10) )
    {
       char buffer[32];
-      sprintf(buffer, "NUMPLAYERS %d", _global->numPlayers);
-      write(server_socket, buffer, strlen(buffer));
+		SAFE_WRITE(server_socket, "NUMPLAYERS %d", env.numGamePlayers)
    }
    else if (! strncmp(net_command, "PLAYERNAME", 10) )
    {
       int my_number;
       char buffer[128];
       sscanf( &(net_command[11]), "%d", &my_number);
-      if ( (my_number >= 0) && (my_number < _global->numPlayers) )
-      {
-           sprintf(buffer, "PLAYERNAME %d %s", my_number, _global->players[my_number]->getName() );
-           write(server_socket, buffer, strlen(buffer));
-      }
-   }
+      if ( (my_number >= 0) && (my_number < env.numGamePlayers) )
+      		SAFE_WRITE(server_socket, "PLAYERNAME %d %s", my_number,
+						env.players[my_number]->getName())
+	}
 
    // how many rounds are we playing
    else if (! strncmp(net_command, "ROUNDS", 6) )
    {
        char buffer[64];
-       sprintf(buffer, "ROUNDS %d %d", (int) _global->rounds, _global->currentround);
-       write(server_socket, buffer, strlen(buffer));
+		SAFE_WRITE(server_socket, "ROUNDS %d %d", env.rounds,
+					global.currentround);
    }
    // send back the position of each tank
    else if (! strncmp(net_command, "TANKPOSITION", 12) )
@@ -4756,35 +1151,34 @@ int PLAYER::Execute_Network_Command(int my_turn)
       int count;
 
       sscanf( &(net_command[13]), "%d", &count);
-      if ( (count >= 0) && (count < _global->numPlayers) )
-      { 
-          if (_global->players[count]->tank)
-          {
-             sprintf(buffer, "TANKPOSITION %d %d %d", count, (int) _global->players[count]->tank->x,
-                 (int) _global->players[count]->tank->y);
-             write(server_socket, buffer, strlen(buffer));
-          }
-      }
+      if ( (count >= 0) && (count < env.numGamePlayers)
+		&& (env.players[count]->tank) )
+			SAFE_WRITE(server_socket, "TANKPOSITION %d %d %d", count,
+						(int) env.players[count]->tank->x,
+						(int) env.players[count]->tank->y)
    }
-  
+
    // send back the surface height of the dirt
    else if (! strncmp(net_command, "SURFACE", 7) )
    {
-       char buffer[32];
-       int x;
-
-       sscanf( &(net_command[8]), "%d", &x);
-       if ( (x >= 0) && (x < _global->screenWidth) )
-       {
-           sprintf(buffer, "SURFACE %d %d", x, _env->surface[x]);
-           write(server_socket, buffer, strlen(buffer));
-       }
-   }
+		char buffer[32];
+		int x;
+
+		sscanf( &(net_command[8]), "%d", &x);
+		if ( (x >= 0) && (x < env.screenWidth) )
+#if defined(ATANKS_IS_BSD)
+			SAFE_WRITE(server_socket, "SURFACE %d %d", x,
+			           global.surface[x].load())
+#else
+			SAFE_WRITE(server_socket, "SURFACE %d %ld", x,
+			           global.surface[x].load())
+#endif // BSD
+	}
    else if (! strncmp(net_command, "SCREEN", 6) )
    {
       char buffer[64];
-      sprintf(buffer, "SCREEN %d %d", _global->screenWidth, _global->screenHeight);
-      write(server_socket, buffer, strlen(buffer));
+		SAFE_WRITE(server_socket, "SCREEN %d %d",
+					env.screenWidth, env.screenHeight)
    }
    else if (! strncmp(net_command, "TEAMS", 5) )
    {
@@ -4792,17 +1186,14 @@ int PLAYER::Execute_Network_Command(int my_turn)
       char buffer[32];
 
       sscanf( &(net_command[6]), "%d", &count);
-      if ( (count < _global->numPlayers) && (count >= 0) )
-      {
-          sprintf(buffer, "TEAM %d %d", count, (int) _global->players[count]->team);
-          write(server_socket, buffer, strlen(buffer));
-      }
+      if ( (count < env.numGamePlayers) && (count >= 0) )
+			SAFE_WRITE(server_socket, "TEAM %d %d", count,
+						(int) env.players[count]->team)
    }
    else if (! strncmp(net_command, "WALLTYPE", 8) )
    {
       char buffer[32];
-      sprintf(buffer, "WALLTYPE %d", _env->current_wallType);
-      write(server_socket, buffer, strlen(buffer));
+		SAFE_WRITE(server_socket, "WALLTYPE %d", env.current_wallType)
    }
    else if (! strncmp(net_command, "WEAPON", 6) )
    {
@@ -4810,18 +1201,1794 @@ int PLAYER::Execute_Network_Command(int my_turn)
       int weapon_number;
       sscanf( &(net_command[7]), "%d", &weapon_number);
       if ( (weapon_number >= 0) && (weapon_number < WEAPONS) )
-      {
-         sprintf(buffer, "WEAPON %d %d", weapon_number, nm[weapon_number]);
-         write(server_socket, buffer, strlen(buffer));
-      }
+		SAFE_WRITE(server_socket, "WEAPON %d %d", weapon_number, nm[weapon_number])
    }
 
    net_command[0] = '\0';
-   return 0;
+
+   return CONTROL_NONE;
+}
+#endif // NETWORK
+
+
+void PLAYER::exitShop()
+{
+	double tmpDM = (ni[ITEM_INTENSITY_AMP] * item[ITEM_INTENSITY_AMP].vals[0])
+	             + (ni[ITEM_VIOLENT_FORCE] * item[ITEM_VIOLENT_FORCE].vals[0]);
+
+	damageMultiplier = 1.0;
+
+	if (tmpDM > 0)
+		damageMultiplier += std::pow(tmpDM, 0.6);
+
+	// All players need small missiles:
+	int32_t max_small_missiles = 500 + (rand() % 500); // max 999
+	nm[SML_MIS] += 50 + (rand() % 50);
+	if (nm[SML_MIS] > max_small_missiles)
+		nm[SML_MIS] = max_small_missiles;
+}
+
+
+/// @brief fill the list of desired items and return the number of damaging weapons
+int32_t PLAYER::generateDesiredList()
+{
+	int32_t result = 0;
+
+	memset(desired, 0, sizeof(int32_t) * THINGS);
+
+	for (int32_t i = 1; i < THINGS; ++i) {
+		if (env.isItemAvailable(i)) {
+			desired[i]  = i;
+			currPref[i] = weapPref[i];
+
+			// No negative prefs:
+			if (currPref[i] < 0)
+				currPref[i] = 0;
+
+			// Count weapons:
+			if ( (i < WEAPONS) && nm[i] )
+				++result;
+		} else
+			desired[i] = 0;
+		// Unavailable items will not be inserted and
+		// the slot is filled with a 0 (small missile)
+		// that will be sorted away.
+	}
+
+	return result;
+}
+
+
+void PLAYER::generatePreferences()
+{
+	double  baseProb    = static_cast<double>(MAX_WEAP_PROBABILITY) / 2.;
+	int32_t currItem    = 0;
+	double  worth       = 0.;
+	bool    isWarhead   = false;
+	int32_t maxWeapPref = 0;
+	int32_t maxItemPref = 0;
+	double  ai_rate     = static_cast<double>(type) / 2. + .5;
+
+	/* --------------------------------------
+	 * --- Generate basic characteristics ---
+	 * --------------------------------------
+	 */
+	defensive          = (static_cast<double>(rand() % 10001) / 5000.)
+	                   - 1.; // [-1;+1]
+	vengeful           = 1 + (rand () % 100); // [1;100]
+	vengeanceThreshold = 0.05
+	                   + (static_cast<double>(rand () % 901)
+	                      / 1000.); // [0.05;0.95]
+	selfPreservation   = static_cast<double>(rand () % 3001) / 1000; // [0;3]
+	painSensitivity    = static_cast<double>(rand () % 3001) / 1000; // [0;3]
+
+	// Now 'defensive' can be modified by team:
+	if (team == TEAM_JEDI) {
+		defensive += static_cast<double>(rand() % 501) / 1000.;
+		if (defensive > 1.25)
+			defensive = 1.25; // + 1.25 is Super Defensive
+	}
+	else if (team == TEAM_SITH) {
+		defensive -= static_cast<double>(rand() % 501) / 1000.;
+		if (defensive < -1.25)
+			defensive = -1.25; // - 1.25 is Super Aggressive
+	}
+
+	/* --------------------------------------------
+	 * --- Generate weapon and item preferences ---
+	 * --------------------------------------------
+	 */
+	if (strcmp(name, "New Player")) {
+		DEBUG_LOG_EMO(name, "Generating preferences (defensive %lf)", defensive)
+		DEBUG_LOG_EMO(name, "---------------------------------------", 0)
+	}
+
+	weapPref[0] = 0; // small missiles are always zero!
+
+	for (int32_t i = 1; i < THINGS; ++i) {
+		worth     = baseProb / -2.;
+		isWarhead = false;
+
+		if (i < WEAPONS) {
+			// Talking about weapons
+			currItem = i;
+			if (weapon[i].warhead
+			  || ( (currItem >= SML_METEOR)
+				&& (currItem <= LRG_LIGHTNING)))
+				isWarhead = true;
+				// Warheads are ignored, this way naturals
+				// are taken out automatically.
+			else {
+
+				int32_t warheads = weapon[currItem].spread;
+
+				// === 1. Damage: ===
+				//--------------------
+				if (weapon[currItem].numSubmunitions > 0) {
+
+					warheads = weapon[currItem].numSubmunitions;
+
+					// Use the total damage for clusters
+					worth = weapon[weapon[currItem].submunition].damage * warheads;
+
+					if ( ( (currItem >= SML_NAPALM) && (currItem <= LRG_NAPALM))
+					  || ( (currItem >= FUNKY_BOMB) && (currItem <= FUNKY_DEATH)) )
+						worth /= (defensive + 2. + ai_rate) / 2.;
+						// These weapons are too unpredictable to be counted full.
+						// But a true offensive useless bot divides only by 1.0
+						// (so not all all, they do not mind) and a true
+						// defensive deadly bot divides by 2.5
+
+					// Napalm Jellies doe damage over time. So their worth
+					// has to reflect that.
+					if ( (currItem >= SML_NAPALM) && (currItem <= LRG_NAPALM))
+						worth *= static_cast<double>(EXPLOSIONFRAMES) / 2.
+						       / static_cast<double>(type);
+
+					if (worth > baseProb)
+						// Or Large Napalm will always be everybody favourite
+						worth = baseProb;
+				} else
+					// Otherwise use spread value with damage. For non-spread
+					// weapons this value is always 1.
+					worth = weapon[currItem].damage * (warheads * 2)
+					      * weapon[currItem].getDelayDiv();
+					// Note: warheads are counted twice, because otherwise spread
+					//       weapons get a by far too low score!
+
+				// 1 Damage is worth 0.5%o of the base probability.
+				worth *= baseProb * 0.0005;
+
+				// === 2. Defensiveness multiplier ===
+				//-------------------------------------
+				// As said above, defensive players avoid spread/cluster
+				// weapons that are too unpredictable. Thus they rate
+				// non-spreads higher:
+				if (warheads == 1)
+					worth *= (defensive + 1.5) * ai_rate;
+
+				// === 3. Dirt weapons ===
+				//-------------------------
+				// Dirt balls and such weapons do no damage and have to be rated
+				// by defensiveness value. Further more the higher the self
+				// preservation value of a bot, the more likely they will try
+				// to bury main damage dealers for one or two rounds of bought
+				// silence.
+				if ( (currItem >= DIRT_BALL) && (currItem <= SMALL_DIRT_SPREAD) )
+					worth = warheads * weapon[currItem].radius
+					      * ai_rate * (defensive + 2.)
+					      * selfPreservation;
+
+				// === 4. Debuff weapons ===
+				//---------------------------
+				// These are the opposite of dirt weapons, they are for the
+				// offensive type with high self preservation.
+				// Note: Although the percent bomb is not a de-buff weapon,
+				// it can hardly be rated any other way, as it has no set yield.
+				if ( (currItem >= PERCENT_BOMB) && (currItem <= REDUCER) )
+					worth = 300. * ai_rate
+					      * -(defensive - 2.)
+					      * selfPreservation;
+
+				// === 5. Shaped weapons are deadly but limited ===
+				//--------------------------------------------------
+				if ( ( (currItem >= SHAPED_CHARGE) && (currItem <= CUTTER) )
+				  || ( DRILLER == currItem ) )
+					worth *= 1.0 - ( ((2. * ai_rate) + (defensive * 5.0)) / 20.0);
+					// useless, full offensive: * 1.15
+					// deadly, full defensive : * 0.45
+
+				// === 6. Rollers and penetrators ===
+				//------------------------------------
+				// These are modified by type, as they *are* useful
+				if ( ( (currItem >= SML_ROLLER) && (currItem <= DTH_ROLLER) )
+				  || ( (currItem >= BURROWER)   && (currItem <= PENETRATOR) ) )
+					worth *= 1.0 + (ai_rate / 5.) + (defensive / 2.);
+
+				// === 7. Tectonics need to be raised! ===
+				//-----------------------------------------
+				// These are nice to damage multiple buried enemies where
+				// penetrators can only reach one.
+				if ( (currItem >= TREMOR) && (currItem <= TECTONIC) )
+					worth *= 2.0 + (ai_rate / 5.) + (defensive / 3.);
+
+				// finally dWorth must not be greater than the 3/4 of MAX_WEAPON_PROBABILITY
+				if (worth > (MAX_WEAP_PROBABILITY * 0.75))
+					worth =  MAX_WEAP_PROBABILITY * 0.75;
+			} // End of "not a warhead"
+		} else {
+			// Talking about items
+			currItem = i - WEAPONS;
+
+			/* Theory:
+			 * The more offensive a bot is, the more likely they go for
+			 * damage amps and repulsor shields.
+			 * The more defensive they are, the more likely they go for
+			 * armour and hard shields.
+			 */
+
+			switch (currItem) {
+				case ITEM_TELEPORT:
+					worth = (defensive - 1.5) * (baseProb / -5.00) * selfPreservation;
+					break;
+				case ITEM_SWAPPER:
+					worth = (defensive - 1.5) * (baseProb / -3.75) * selfPreservation;
+					break;
+				case ITEM_MASS_TELEPORT:
+					worth = (defensive - 1.5) * (baseProb / -1.50) * selfPreservation;
+					break;
+				case ITEM_FAN:
+					worth = 0.0; // useless things!
+					break;
+				case ITEM_VENGEANCE:
+				case ITEM_DYING_WRATH:
+				case ITEM_FATAL_FURY:
+					worth = (defensive + 1.5)
+					      *  static_cast<double>(weapon[(int)item[currItem].vals[0]].damage)
+					      *  item[currItem].vals[1];
+					break;
+				case ITEM_ARMOUR:
+				case ITEM_PLASTEEL:
+					worth = baseProb * ( item[currItem].vals[0] / item[ITEM_PLASTEEL].vals[0])
+					      * ( defensive + 1.25 );
+					break;
+				case ITEM_LGT_SHIELD:
+				case ITEM_MED_SHIELD:
+				case ITEM_HVY_SHIELD:
+					worth = baseProb * ( item[currItem].vals[0] / item[ITEM_HVY_SHIELD].vals[0])
+					      * ( defensive + 1.25 );
+					break;
+				case ITEM_INTENSITY_AMP:
+				case ITEM_VIOLENT_FORCE:
+					worth = baseProb * ( item[currItem].vals[0] / item[ITEM_VIOLENT_FORCE].vals[0])
+					      * ( (-1. * defensive) + 1.25 );
+					break;
+				case ITEM_LGT_REPULSOR_SHIELD:
+				case ITEM_MED_REPULSOR_SHIELD:
+				case ITEM_HVY_REPULSOR_SHIELD:
+					worth = baseProb * ( item[currItem].vals[0] / item[ITEM_HVY_REPULSOR_SHIELD].vals[0])
+					      * ( (-1. * defensive) + 1.25 );
+					break;
+				case ITEM_REPAIRKIT:
+					worth = (baseProb / 12. * ai_rate)
+					      * (defensive + 2.25 + (selfPreservation / 2.));
+					break;
+				case ITEM_PARACHUTE:
+					worth = (baseProb / 10. * ai_rate)
+					      * ( (defensive + 1.5) / 1.5);
+					// Parachutes *are* popular! :)
+					break;
+				case ITEM_SLICKP:
+					worth = baseProb / 25. * ai_rate;
+					break;
+				case ITEM_DIMPLEP:
+					worth = baseProb / 15. * ai_rate;
+					break;
+				case ITEM_FUEL:
+					worth     = -5000; // Bots don't need fuel
+					isWarhead = true;  // Yes, it's a lie. ;-)
+					break;
+				case ITEM_ROCKET:
+					worth     = -5000; // Bots don't use rockets
+					isWarhead = true;  // The cake is a lie!
+					break;
+				case ITEM_SDI:
+					worth = (baseProb / 13. * ai_rate)
+					      * ( (defensive + 2.25 + selfPreservation) / 1.25);
+					break;
+				default:
+					cerr << "Error: Unhandled item " << currItem;
+					cerr << "       in generatePreferences()!" << endl;
+					worth = baseProb / ai_rate;
+			}
+
+
+			// worth must not be greater than the half of MAX_WEAPON_PROBABILITY
+			if (worth > (MAX_WEAP_PROBABILITY / 2))
+				worth =  MAX_WEAP_PROBABILITY / 2;
+		}
+
+		// Boost the tiny ones:
+		if (worth < (MAX_WEAP_PROBABILITY / 25.0))
+			worth =  MAX_WEAP_PROBABILITY / 25.0; // Which is very very little...
+		if (worth < (MAX_WEAP_PROBABILITY / 8))
+			// allow to double (more or less)
+			worth += static_cast<double>(rand() % static_cast<int32_t>(std::abs(worth)));
+
+		// But don't overdo either:
+		if (worth >  MAX_WEAP_PROBABILITY)
+			worth =  MAX_WEAP_PROBABILITY;
+
+		if (isWarhead)
+			weapPref[i] = 0;	// It will not get any slot!
+		else
+			weapPref[i] = ROUND(worth);
+
+		// Count statistical values
+		if ((i < WEAPONS) && (weapPref[i] > maxWeapPref))
+			maxWeapPref = weapPref[i];
+		if ((i >= WEAPONS) && (weapPref[i] > maxItemPref))
+			maxItemPref = weapPref[i];
+
+		if (strcmp(name, "New Player")) {
+			DEBUG_LOG_EMO(name, "%23s (%6s): %5d",
+					i < WEAPONS ? weapon[i].getName() : item[i - WEAPONS].getName(),
+					i < WEAPONS ? "weapon" : "item",
+					weapPref[i])
+		}
+	} // end of looping THINGS
+
+	// If the maximum preferences are too low, they have to be augmented
+	if (maxWeapPref < MAX_WEAP_PROBABILITY) {
+		worth = static_cast<double>(MAX_WEAP_PROBABILITY)
+		      / static_cast<double>(maxWeapPref);
+
+		for (int32_t i = 1; i < WEAPONS; ++i) {
+			if (weapPref[i] > (MAX_WEAP_PROBABILITY / 100.0)) {
+				weapPref[i] = ROUND(worth * weapPref[i]);
+				if (strcmp(name, "New Player")) {
+					DEBUG_LOG_EMO(name, "%23s (%6s) amplified to: %5d",
+							weapon[i].getName(), "weapon", weapPref[i])
+				}
+			}
+		}
+	}
+
+	if (maxItemPref < (MAX_WEAP_PROBABILITY * 0.75) ) {
+		worth = static_cast<double>(MAX_WEAP_PROBABILITY) * 0.75
+		      / static_cast<double>(maxItemPref);
+
+		for (int32_t i = WEAPONS; i < THINGS; ++i) {
+			if (weapPref[i] > (MAX_WEAP_PROBABILITY / 100.0)) {
+				weapPref[i] = ROUND(worth * weapPref[i]);
+				if (strcmp(name, "New Player")) {
+					DEBUG_LOG_EMO(name, "%23s (%6s) amplified to: %5d",
+							item[i - WEAPONS].getName(), "item", weapPref[i])
+				}
+			}
+		}
+	}
+
+	if (strcmp(name, "New Player"))
+		DEBUG_LOG_EMO(name, "=======================================", 0)
+}
+
+
+int PLAYER::getAmpValue()
+{
+	double amp_val = ni[ITEM_INTENSITY_AMP] * item[ITEM_INTENSITY_AMP].vals[0];
+	double vio_val = ni[ITEM_VIOLENT_FORCE] * item[ITEM_VIOLENT_FORCE].vals[0];
+	return ROUNDu(  (amp_val + vio_val)
+	              / static_cast<double>(item[ITEM_VIOLENT_FORCE].vals[0]));
+}
+
+
+int PLAYER::getArmourValue()
+{
+	double arm_val = ni[ITEM_ARMOUR]   * item[ITEM_ARMOUR].vals[0];
+	double pla_val = ni[ITEM_PLASTEEL] * item[ITEM_PLASTEEL].vals[0];
+	return ROUNDu(  (arm_val + pla_val)
+	              / static_cast<double>(item[ITEM_PLASTEEL].vals[0]) );
+}
+
+
+int PLAYER::getBoostValue()
+{
+	return (getAmpValue() + getArmourValue());
+}
+
+
+/// @brief return the item preference of item @a idx or -1 if @a idx is out of
+/// range
+/// Note: This uses the static weapPref instead of the adapted currPref,
+///       because it is used by AICore for point calculation.
+int32_t PLAYER::getItemPref(int32_t idx)
+{
+	if ( (idx > -1) && (idx < ITEMS) )
+		return weapPref[WEAPONS + idx];
+	return -1;
+}
+
+
+int32_t PLAYER::getMoneyToSave(bool first_look)
+{
+	// If this is the first look in a shopping round,
+	// the list of items to save money for must be built:
+	if (first_look) {
+		int32_t avgPref     = 0;
+		int32_t prefCount   = 0;
+		int32_t prefLimit   = 0;
+		memset(saveMoneyFor, 0, sizeof(int32_t) * THINGS);
+
+		// if the preferences are exceptionally low, a div by 0
+		// might occur, so it has to be made dynamic:
+		for (int32_t i = 0; i < THINGS; ++i) {
+			if (currPref[i] > prefLimit) {
+				prefLimit += currPref[i];
+				prefCount++;
+			}
+		}
+
+		prefLimit /= prefCount ? prefCount : 1;
+		prefCount  = 0;
+
+		// Now it is guaranteed, that prefCount and avgPref
+		// will result in values > 0.
+		for (int32_t i = 0; i < THINGS; ++i) {
+			if (currPref[i] > prefLimit) {
+				prefCount++;
+				avgPref += currPref[i];
+			}
+		}
+
+		// Complete the average preference of the most valuable weapons:
+		avgPref /= prefCount ? prefCount : 1;
+
+		// Now go through the list and add everything above the
+		// average into the save money list, if the amount in stock
+		// is too low:
+		for (int32_t i = 0; i < THINGS; ++i) {
+			int32_t j = i - WEAPONS; // short cut
+			if ( (currPref[i] > avgPref)
+			  && ( ( (i <  WEAPONS) && (nm[i] < weapon[i].amt) )
+			    || ( (j == ITEM_VIOLENT_FORCE) && needAmp)
+			    || ( (j == ITEM_PLASTEEL)      && needArmour) ) ) {
+				saveMoneyFor[i] = i <  WEAPONS ? weapon[i].cost : item[j].cost;
+				DEBUG_LOG_FIN(name, " => Save money for %s!",
+				              i <  WEAPONS
+				                ? weapon[i].getName()
+				                : item[j].getName())
+			} // End of having a big enough preference
+		} // End of looping THINGS
+	} // End of building safe-for-list
+
+
+	// moneyToSafe can be easily generated (and regenerated)
+	// by walking through the list of things currently saved
+	// money for:
+	double  moneyToSave = 0.;
+	double  wanted      = 0.;
+	double  max_cost    = 0.; // The most expensive item is counted twice
+	for (int32_t i = 0; i < THINGS; ++i) {
+		int32_t j = i - WEAPONS; // short cut
+
+		if (saveMoneyFor[i] > 0) {
+			// Still needed?
+			if ( ( (i <  WEAPONS) && (nm[i] < weapon[i].amt) )
+			  || ( (j == ITEM_VIOLENT_FORCE) && needAmp)
+			  || ( (j == ITEM_PLASTEEL)      && needArmour) ) {
+			  	moneyToSave += saveMoneyFor[i];
+			  	wanted      += 1.;
+				if (saveMoneyFor[i] > max_cost)
+					max_cost = saveMoneyFor[i];
+			} else
+				// nope...
+				saveMoneyFor[i] = 0;
+		}
+	}
+
+	// If anything is wanted, the most expensive item is counted twice.
+	// This is done so the bots do not consider having enough money
+	// too early, just like humans would.
+	if (max_cost > 1.) {
+		moneyToSave += max_cost;
+		wanted      += 1.;
+
+		// The average money to save modified by the player type
+		// is the result:
+		moneyToSave = (moneyToSave / wanted)
+		            * (1. + (static_cast<double>(LAST_PLAYER_TYPE - type) / 10.));
+	}
+
+	/* Results for Armageddon only @ 100k credits:
+	 * (wanted is 1 in this test case)
+	 * Useless: 100,000 * (1 + ( (6 - 1) / 10)) = 100,000 * 1.5 = 150,000
+	 * Deadly : 100,000 * (1 + ( (6 - 5) / 10)) = 100,000 * 1.1 = 110,000
+	 */
+
+	// Whenever moneyToSave is less than the money owned, boostBought is reset
+	if (first_look && (money > ROUND(moneyToSave)) ) {
+		boostBought  = 0; // Let's go!
+		shieldBought = 0;
+	}
+
+	return ROUND(moneyToSave);
+}
+
+
+// return the player name
+const char* PLAYER::getName () const
+{
+	return name;
+}
+
+
+// This function checks for incoming data from a client.
+// If data is coming in, we put the incoming data in the net_command
+// variable. If the socket connection is broken, then we will
+// close the socket and hand control over to the AI.
+bool PLAYER::getNetCmd()
+{
+#ifdef NETWORK
+	if (Check_For_Incoming_Data(server_socket)) {
+		// we have something coming down the pipe
+		memset(net_command, '\0', NET_COMMAND_SIZE);    // clear buffer
+		size_t status = read(server_socket, net_command, NET_COMMAND_SIZE);
+		if (! status) {
+			// connection is broken
+			close(server_socket);
+			type = DEADLY_PLAYER;
+			printf("%s lost network connection. Returning control to AI.\n", name);
+			return false;
+		} else {
+			// we got data
+			net_command[NET_COMMAND_SIZE - 1] = '\0';
+			Trim_Newline(net_command);
+		}
+	}
+#endif // NETWORK
+	return true;
+}
+
+
+/** @brief Get one entry of the opponent memory or the last one attacked
+  * @param[in] idx Index of the opponent memory to get, or -1 to get the last attacked.
+**/
+sOpponent* PLAYER::getOppMem(int32_t idx)
+{
+	// regular memory
+	if ( (idx > -1) && (idx < oppCount) )
+		return &opponents[idx];
+
+	// or the last attacked
+	else if (-1 == idx)
+		return last_opponent;
+
+	// or invalid.
+	return nullptr;
+}
+
+
+// returns a static string to the player's team name
+const char* PLAYER::getTeamName() const
+{
+	static char team_name[9] = { 0 };
+
+	switch (team) {
+		case TEAM_JEDI:
+			snprintf(team_name, 8, "%s", "Jedi");
+			break;
+		case TEAM_NEUTRAL:
+			snprintf(team_name, 8, "%s", "Neutral");
+			break;
+		case TEAM_SITH:
+			snprintf(team_name, 8, "%s", "Sith");
+			break;
+		case TEAM_COUNT:
+		default:
+			snprintf(team_name, 8, "%s", "* N/A *");
+			break;
+	}
+
+	return team_name;
+}
+
+
+/// @brief return the weapon preference of weapon @a idx or -1 if @a idx is out
+/// of range.
+/// Note: This uses the static weapPref instead of the adapted currPref,
+///       because it is used by AICore for point calculation.
+int32_t PLAYER::getWeapPref(int32_t idx)
+{
+	if ( (idx > -1) && (idx < WEAPONS) )
+		return weapPref[idx];
+	return -1;
+}
+
+
+eControl PLAYER::humanControls (AICore* aicore)
+{
+	bool     moved  = false;
+	eControl status = CONTROL_NONE;
+
+	// Keyboard control in aim stage
+	if ( (global.stage == STAGE_AIM) && tank) {
+		if ( (key[KEY_LEFT] || key[KEY_A])
+		  && !ctrlUsedUp && (tank->a < 270) ) {
+			if (has_shift_pressed)
+				tank->a = std::min(tank->a + 5, 270);
+			else
+				tank->a++;
+			global.updateMenu = 1;
+			if (has_ctrl_pressed)
+				ctrlUsedUp = true;
+		}
+
+		if ( (key[KEY_RIGHT] || key[KEY_D])
+		  && !ctrlUsedUp && (tank->a > 90) ) {
+			if (has_shift_pressed)
+				tank->a = std::max(tank->a - 5, 90);
+			else
+				tank->a--;
+			global.updateMenu = 1;
+			if (has_ctrl_pressed)
+				ctrlUsedUp = true;
+		}
+
+		if ( (key[KEY_DOWN] || key[KEY_S])
+		  && !ctrlUsedUp && (tank->p > 0) ) {
+			if (has_shift_pressed)
+				tank->p = std::max(tank->p - 25, 0);
+			else
+				tank->p -= 5;
+			global.updateMenu = 1;
+			if (has_ctrl_pressed)
+				ctrlUsedUp = true;
+		}
+
+		if ( (key[KEY_UP] || key[KEY_W])
+		  && !ctrlUsedUp && (tank->p < MAX_POWER) ) {
+			if (has_shift_pressed)
+				tank->p = std::min(tank->p + 25, MAX_POWER);
+			else
+				tank->p += 5;
+			global.updateMenu = 1;
+			if (has_ctrl_pressed)
+				ctrlUsedUp = true;
+		}
+
+		if ( (key[KEY_PGUP] || key[KEY_R])
+		  && !ctrlUsedUp && (tank->p < MAX_POWER) ) {
+			tank->p += 100;
+			if (tank->p > MAX_POWER)
+				tank->p = MAX_POWER;
+			global.updateMenu = 1;
+			if (has_ctrl_pressed)
+				ctrlUsedUp = true;
+		}
+
+		if ( (key[KEY_PGDN] || key[KEY_F])
+		  && !ctrlUsedUp && (tank->p > 0) ) {
+			tank->p -= 100;
+			if (tank->p < 0)
+				tank->p = 0;
+			global.updateMenu = 1;
+			if (has_ctrl_pressed)
+				ctrlUsedUp = true;
+		}
+	}
+
+	// See whether there is a new key press
+    if (! k) {
+		if ( keypressed() ) {
+			k = readkey();
+			K = k >> 8;
+		}
+    }
+
+	// If anything is newly there, make it happen
+	if (K) {
+		status = CONTROL_OTHER;
+
+		if ( (global.stage == STAGE_AIM) && tank) {
+			if (K == KEY_N) {
+				tank->a = 180;
+				global.updateMenu = 1;
+				K = 0;
+			}
+
+			if ( (K == KEY_TAB) || (K == KEY_C) ) {
+				global.updateMenu = 1;
+				bool done = false;
+				while (!done) {
+					if (++tank->cw >= THINGS)
+						tank->cw = 0;
+
+					if ( ( (tank->cw < WEAPONS)
+						&& tank->player->nm[tank->cw])
+					  || ( (tank->cw >= WEAPONS)
+						&& item[tank->cw - WEAPONS].selectable
+						&& tank->player->ni[tank->cw - WEAPONS] ) )
+						done = true;
+				}
+				changed_weapon = false;
+				K = 0;
+			}
+
+			if ( (K == KEY_BACKSPACE) || (K == KEY_Z) ) {
+				global.updateMenu = 1;
+				bool done = false;
+				while (!done) {
+					if (--tank->cw < 0)
+						tank->cw = THINGS - 1;
+
+					if ( ( (tank->cw < WEAPONS)
+						&& tank->player->nm[tank->cw])
+					  || ( (tank->cw >= WEAPONS)
+						&& item[tank->cw - WEAPONS].selectable
+						&& tank->player->ni[tank->cw - WEAPONS] ) )
+						done = true;
+				}
+				changed_weapon = false;
+				K = 0;
+			}
+
+			// put the tank under computer control
+			if (K == KEY_F10) {
+				type = PART_TIME_BOT;
+				K = 0;
+				return (computerControls(aicore, false));
+			}
+
+			// move the tank
+			if ( (K == KEY_COMMA) || (K == KEY_H) )
+				moved = tank->moveTank(DIR_LEFT);
+			if ( (K == KEY_STOP) || (K == KEY_J) )
+				moved = tank->moveTank(DIR_RIGHT);
+
+			if (moved) {
+				global.updateMenu = 1;
+				K = 0;
+			}
+
+			// Fire Weapon
+			if ( (K == KEY_SPACE)
+			  && ( ( (tank->cw <  WEAPONS)
+				  && (tank->player->nm[tank->cw]))
+				|| ( (tank->cw >= WEAPONS)
+				  && (tank->player->ni[tank->cw - WEAPONS]) ) ) ) {
+
+				gloating = false;
+				status = CONTROL_FIRE;
+				changed_weapon = false;
+				K = 0;
+			}
+		} // end of being in aim satge and having a tank
+	} // End of havig a key
+
+	return status;
+}
+
+
+void PLAYER::initialise (bool loaded_game)
+{
+	// Initialize basic values if this is not loaded
+	if (!loaded_game) {
+		nm[0] = MAX_ITEMS_IN_STOCK;
+		memset(nm, 0, sizeof(int32_t) * WEAPONS);
+		memset(ni, 0, sizeof(int32_t) * ITEMS);
+
+		kills  = 0;
+		killed = 0;
+		score  = 0;
+	}
+
+	last_opponent = nullptr;
+	tank          = nullptr;
+}
+
+
+/// @brief read player data from a dump file.
+bool PLAYER::load_from_file (FILE *file)
+{
+	if (!file)
+		return false;
+
+	char  line[MAX_CONFIG_LINE  + 1] = { 0 };
+	char  field[MAX_CONFIG_LINE + 1] = { 0 };
+	char  value[MAX_CONFIG_LINE + 1] = { 0 };
+	char* result                     = nullptr;
+
+	setlocale(LC_NUMERIC, "C");
+
+	// read until we hit line "*PLAYER*" or "***" or EOF
+	do {
+		result = fgets(line, MAX_CONFIG_LINE, file);
+		if ( !result
+		  || !strncmp(line, "***", 3) )
+			// eof OR end of record
+			return false;
+	} while ( strncmp(line, "*PLAYER*", 8) );
+
+	bool  done = false;
+
+	while (result && !done) {
+		// read a line
+		memset(line, '\0', MAX_CONFIG_LINE);
+		if ( ( result = fgets(line, MAX_CONFIG_LINE, file) ) ) {
+
+			// if we hit end of the record, stop
+			if (! strncmp(line, "***", 3) ) {
+				done = true;
+				continue; // This exits the loop as well
+			}
+
+			// strip newline character
+			int32_t line_length = strlen(line);
+			while ( line[line_length - 1] == '\n') {
+				line[line_length - 1] = '\0';
+				line_length--;
+			}
+
+			// find equal sign
+			int32_t equal_position = 1;
+			while ( ( equal_position < line_length )
+				 && ( line[equal_position] != '='  ) )
+				equal_position++;
+
+			// make sure the equal sign position is valid
+			if (line[equal_position] != '=')
+				continue; // Go to next line
+
+			// separate field from value
+			memset(field, '\0', MAX_CONFIG_LINE);
+			memset(value, '\0', MAX_CONFIG_LINE);
+			strncpy(field, line, equal_position);
+			strncpy(value, &( line[equal_position + 1] ), MAX_CONFIG_LINE);
+
+			// check which field we have and process value
+			if (!strcasecmp(field, "NAME") )
+				strncpy(name, value, NAME_LEN);
+			else if (!strcasecmp(field, "COLOR") ) {
+				sscanf(value, "%d", &color);
+			} else if (!strcasecmp(field, "DEFENSIVE") )
+				sscanf(value, "%lf", &defensive);
+			else if (!strcasecmp(field, "PAINSENSITIVITY") )
+				sscanf(value, "%lf", &painSensitivity);
+			else if (!strcasecmp(field, "PLAYED") )
+				sscanf(value, "%u", &played);
+			else if (!strcasecmp(field, "PREFTYPE") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				if ( (val >= 0) && (val <= ALWAYS_PREF))
+					preftype = static_cast<playerPrefType>(val);
+			} else if (!strcasecmp(field, "SELFPRESERVATION") )
+				sscanf(value, "%lf", &selfPreservation);
+			else if (!strcasecmp(field, "TANK_BITMAP") )
+				sscanf(value, "%d", &tankbitmap);
+			else if (!strcasecmp(field, "TEAM") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				if ( (val >= 0) && (val <= TEAM_JEDI) )
+					team = static_cast<eTeamTypes>(val);
+			} else if (!strcasecmp(field, "TYPE") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+
+				if ( (val >= HUMAN_PLAYER) && (val <= LAST_PLAYER_TYPE))
+					type = static_cast<playerType>(val);
+
+				// make sure previous human players are restored as humans
+				if (type == PART_TIME_BOT)
+					type = HUMAN_PLAYER;
+
+			} else if (!strcasecmp(field, "TYPESAVED") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val);
+				if ( (val >= HUMAN_PLAYER) && (val <= LAST_PLAYER_TYPE)) {
+					type_saved = static_cast<playerType>(val);
+					if (type_saved > HUMAN_PLAYER)
+						type = type_saved;
+				}
+			} else if (!strcasecmp(field, "VENGEANCETHRESHOLD") ) {
+				sscanf(value, "%lf", &vengeanceThreshold);
+				// fix old configs
+				if (vengeanceThreshold < 0.05)
+					vengeanceThreshold = 0.05
+									   + (static_cast<double>(rand () % 901)
+										  / 1000.); // [0.05;0.95]
+				if (vengeanceThreshold > 0.95)
+					vengeanceThreshold = 0.95;
+			} else if (!strcasecmp(field, "VENGEFUL") ) {
+				sscanf(value, "%d", &vengeful);
+				// fix old configs
+				if (vengeful < 1)
+					vengeful = 1 + (rand () % 100); // [1;100]
+				if (vengeful > 100)
+					vengeful = 100;
+			} else if (!strcasecmp(field, "WON") )
+				sscanf(value, "%u", &won);
+			else if (!strcasecmp(field, "WEAPONPREFERENCES") ) {
+				int32_t wp_index = -1;
+				int32_t wp_value = -1;
+				sscanf(value, "%d %d", &wp_index, &wp_value);
+				if ( (wp_index < THINGS) && (wp_index >= 0) )
+					weapPref[wp_index] = wp_value;
+			} // end of valid data line
+		} // end of if we read a line properly
+	} // end of while not done
+
+	return true;
+}
+
+
+void PLAYER::load_game_data(FILE* file)
+{
+	if (!file)
+		return;
+
+	char  line[MAX_CONFIG_LINE  + 1] = { 0 };
+	char  field[MAX_CONFIG_LINE + 1] = { 0 };
+	char  value[MAX_CONFIG_LINE + 1] = { 0 };
+	char* result                     = nullptr;
+	bool  done                       = false;
+	bool  has_pref_loaded            = false;
+
+	setlocale(LC_NUMERIC, "C");
+
+
+	do {
+		// read a line
+		memset(line, '\0', MAX_CONFIG_LINE);
+		if ( ( result = fgets(line, MAX_CONFIG_LINE, file) ) ) {
+
+			// if we hit end of the record, stop
+			if (! strncmp(line, "***", 3) ) {
+				done = true;
+				continue; // This exits the loop as well
+			}
+
+			// strip newline character
+			int32_t line_length = strlen(line);
+			while ( line[line_length - 1] == '\n') {
+				line[line_length - 1] = '\0';
+				line_length--;
+			}
+
+			// find equal sign
+			int32_t equal_position = 1;
+			while ( ( equal_position < line_length )
+				 && ( line[equal_position] != '='  ) )
+				equal_position++;
+
+			// make sure the equal sign position is valid
+			if (line[equal_position] != '=')
+				continue; // Go to next line
+
+			// separate field from value
+			memset(field, '\0', MAX_CONFIG_LINE);
+			memset(value, '\0', MAX_CONFIG_LINE);
+			strncpy(field, line, equal_position);
+			strncpy(value, &( line[equal_position + 1] ), MAX_CONFIG_LINE);
+
+			// check which field we have and process value
+			if (!strcasecmp(field, "DEFENSIVE") )
+				sscanf(value, "%lf", &defensive);
+			else if (!strcasecmp(field, "PAINSENSITIVITY") )
+				sscanf(value, "%lf", &painSensitivity);
+			else if (!strcasecmp(field, "KILLED") )
+                    sscanf(value, "%d", &killed );
+			else if (!strcasecmp(field, "KILLS") )
+                    sscanf(value, "%d", &kills );
+			else if (!strcasecmp(field, "MONEY") )
+                    sscanf(value, "%d", &money );
+			else if (!strcasecmp(field, "SCORE") )
+                    sscanf(value, "%d", &score );
+			else if (!strcasecmp(field, "SELFPRESERVATION") )
+				sscanf(value, "%lf", &selfPreservation);
+			else if (!strcasecmp(field, "TYPE") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val );
+				if ( (val >= HUMAN_PLAYER) && (val < LAST_PLAYER_TYPE) )
+					type = static_cast<playerType>(val);
+			} else if (!strcasecmp(field, "TYPESAVED") ) {
+				int32_t val = 0;
+				sscanf(value, "%d", &val );
+				if ( (val >= HUMAN_PLAYER) && (val < LAST_PLAYER_TYPE) )
+					type_saved = static_cast<playerType>(val);
+			} else if (!strcasecmp(field, "VENGEANCETHRESHOLD") ) {
+				sscanf(value, "%lf", &vengeanceThreshold);
+				// fix old configs
+				if (vengeanceThreshold < 0.05)
+					vengeanceThreshold = 0.05
+									   + (static_cast<double>(rand () % 901)
+										  / 1000.); // [0.05;0.95]
+				if (vengeanceThreshold > 0.95)
+					vengeanceThreshold = 0.95;
+			} else if (!strcasecmp(field, "VENGEFUL") ) {
+				sscanf(value, "%d", &vengeful);
+				// fix old configs
+				if (vengeful < 1)
+					vengeful = 1 + (rand () % 100); // [1;100]
+				if (vengeful > 100)
+					vengeful = 100;
+			}
+
+			// Preferences - saved if "PERPLAY_PREF" - type player.
+			else if (!strcasecmp(field, "WEAPONPREFERENCES")) {
+				int32_t prf_idx = -1;
+				int32_t prf_val = -1;
+				sscanf(value, "%d %d", &prf_idx, &prf_val);
+				if ( (prf_idx > -1) && (prf_idx < THINGS) ) {
+					weapPref[prf_idx] = prf_val;
+					has_pref_loaded   = true; // to separate old versus new save games
+				}
+			}
+
+			// Inventory of the weapons
+			else if (!strcasecmp(field, "WEAPON")) {
+				int32_t weap_idx = -1;
+				int32_t weap_val = -1;
+				sscanf(value, "%d %d", &weap_idx, &weap_val);
+				if ( (weap_idx > -1) && (weap_idx < WEAPONS) )
+					nm[weap_idx] = weap_val;
+			}
+
+			// Inventory of the items
+			else if (!strcasecmp(field, "ITEM")) {
+				int32_t item_idx = -1;
+				int32_t item_val = -1;
+				sscanf(value, "%d %d", &item_idx, &item_val);
+				if ( (item_idx > -1) && (item_idx < ITEMS) )
+					ni[item_idx] = item_val;
+			}
+
+			// Opponents Memory
+			else if (!strcasecmp(field, "OPPCOUNT") ) {
+				int32_t safed_count = 0;
+				sscanf(value, "%d", &safed_count );
+
+				// prepare the memory
+				if (opponents) {
+					delete [] opponents;
+					opponents = nullptr;
+				}
+
+				if (safed_count) {
+					try {
+						oppCount  = safed_count;
+						opponents = new opp_t[oppCount];
+					} catch (std::exception &e) {
+						cerr << "ERROR: Unable to allocate ";
+						cerr << (sizeof(opp_t) * oppCount);
+						cerr << " bytes for opponents array!" << endl;
+						cerr << "ERROR: " << e.what() << endl;
+						oppCount = 0;
+					}
+				} else
+					oppCount = 0;
+			} // end of oppcount handling
+			else if (!strcasecmp(field, "OPPMEM_INDX") ) {
+				int32_t opp_idx = -1;
+				int32_t opp_val = -1;
+				sscanf(value, "%d %d", &opp_idx, &opp_val);
+				if ( (opp_idx > -1) && (opp_idx < oppCount) ) {
+					opponents[opp_idx].index    = opp_val;
+					opponents[opp_idx].opponent = env.allPlayers[opp_val];
+				}
+			} else if (!strcasecmp(field, "OPPMEM_DDEA") ) {
+				int32_t opp_idx = -1;
+				int32_t opp_val = -1;
+				sscanf(value, "%d %d", &opp_idx, &opp_val);
+				if ( (opp_idx > -1) && (opp_idx < oppCount) )
+					opponents[opp_idx].damage_from = opp_val;
+			} else if (!strcasecmp(field, "OPPMEM_DDON") ) {
+				int32_t opp_idx = -1;
+				int32_t opp_val = -1;
+				sscanf(value, "%d %d", &opp_idx, &opp_val);
+				if ( (opp_idx > -1) && (opp_idx < oppCount) )
+					opponents[opp_idx].damage_to = opp_val;
+			} else if (!strcasecmp(field, "OPPMEM_FEAR") ) {
+				int32_t opp_idx = -1;
+				double  opp_val = 0.;
+				sscanf(value, "%d %lf", &opp_idx, &opp_val);
+				if ( (opp_idx > -1) && (opp_idx < oppCount) )
+					opponents[opp_idx].fear = opp_val;
+			} else if (!strcasecmp(field, "OPPMEM_KIME") ) {
+				int32_t opp_idx = -1;
+				int32_t opp_val = -1;
+				sscanf(value, "%d %d", &opp_idx, &opp_val);
+				if ( (opp_idx > -1) && (opp_idx < oppCount) )
+					opponents[opp_idx].killed_me = opp_val;
+			} else if (!strcasecmp(field, "OPPMEM_KITH") ) {
+				int32_t opp_idx = -1;
+				int32_t opp_val = -1;
+				sscanf(value, "%d %d", &opp_idx, &opp_val);
+				if ( (opp_idx > -1) && (opp_idx < oppCount) )
+					opponents[opp_idx].killed_them = opp_val;
+			}
+
+
+		} // End of having a line
+	} while (result && !done);
+	// End of reading player section
+
+
+	// For backwards compatibility the preferences must be generated
+	// if this is a PERPLAY type player but no preferences got saved.
+	// This might be the case for very old save games.
+	if (!has_pref_loaded
+	  && (PERPLAY_PREF == preftype)
+	  && (type != HUMAN_PLAYER) )
+		generatePreferences();
+}
+
+
+/// @brief reserve memory for the opponents array and fill it
+void PLAYER::newGame()
+{
+	if (env.numGamePlayers) {
+
+		if (opponents) {
+			delete [] opponents;
+			opponents = nullptr;
+		}
+
+		try {
+			oppCount  = env.numGamePlayers;
+			opponents = new opp_t[oppCount];
+		} catch (std::exception &e) {
+			cerr << "ERROR: Unable to allocate " << (sizeof(opp_t) * oppCount);
+			cerr << " bytes for opponents array!" << endl;
+			cerr << "ERROR: " << e.what() << endl;
+			oppCount  = 0;
+		}
+	}
+
+	if (oppCount) {
+		for (int32_t i = 0; i < oppCount; ++i) {
+			opponents[i].opponent = env.players[i];
+			opponents[i].index    = env.players[i]->index;
+		}
+	}
+}
+
+
+// run this at the begining of each turn
+void PLAYER::newRound()
+{
+	// if the player is under computer control, give it back to the player
+	if ( type == PART_TIME_BOT )
+		type = HUMAN_PLAYER;
+
+	if (!tank) {
+		try {
+			tank = new TANK();
+			tank->player = this;
+		} catch (std::exception &e) {
+			cerr << "FATAL: Error allocating memory for TANK in player.cpp:";
+			cerr << __LINE__ << " : " << e.what() << endl;
+			global.set_command(GLOBAL_COMMAND_QUIT);
+		}
+	}
+	// tank->newRound() doesn't need to be called, because
+	// the game loop will do that on tank placement.
+
+	// if we are playing in a campaign, raise the AI level for every 20% played
+	// rounds, so that useless players become deadly at 80% played rounds
+	if (env.campaign_mode
+	  && (global.currentround < env.nextCampaignRound)
+	  && (type > HUMAN_PLAYER)
+	  && (type < DEADLY_PLAYER) )
+		++type;
+
+	// reset some basic values
+	changed_weapon    = false;
+	time_left_to_fire = env.maxFireTime;
+	skip_me           = false;
+	last_shield_used  = 0;
+
+	// Save damage from opponents if there was some not processed.
+	// Although this would be done automatically once the AI takes
+	// this player over the next time, lingering damage from the
+	// last round can lead to panic actions and/or revenge actions
+	// against players, who haven't fired, yet.
+	// Noting the damage will raise the probability, but only once
+	// the opponent had their first shot.
+	for (int32_t i = 0; i < oppCount; ++i) {
+		if (opponents[i].damage_last > 0) {
+			opponents[i].damage_from += opponents[i].damage_last;
+			opponents[i].damage_last  = 0;
+		}
+	}
+}
+
+
+void PLAYER::noteDamageFrom(PLAYER* opponent, int32_t damage, bool destroyed)
+{
+	if (opponent) {
+		int32_t idx       = oppCount;
+		int32_t max_score = 0;
+
+		for (int32_t i = 0; i < oppCount; ++i) {
+			if (opponents[i].revenge_dmg > max_score)
+				max_score = opponents[i].revenge_dmg;
+			if (opponents[i].opponent == opponent)
+				idx = i;
+		}
+
+		if ( idx < oppCount ) {
+			opponents[idx].damage_last += damage;
+			if (destroyed)
+				opponents[idx].killed_me++;
+
+			// If this one has the new top score and is not
+			// the current revenge player, get a message out
+			int32_t rev_dmg = opponents[idx].damage_last
+			                + opponents[idx].revenge_dmg;
+
+			if ( (opponents[idx].opponent != this)
+			  && (opponents[idx].opponent != revenge)
+			  && (rev_dmg > (vengeanceThreshold * tank->getMaxLife()))
+			  && (rev_dmg > max_score) ) {
+
+				revenge = opponents[idx].opponent;
+
+				if (!global.skippingComputerPlay ) {
+					try {
+						new FLOATTEXT(selectRevengePhrase(),
+										tank->x, tank->y - 30, .0, -.4, color,
+										CENTRE, TS_NO_SWAY, 300);
+					} catch (...) {
+						perror ( "player.cpp: Failed to allocate memory for"
+								 " revenge text in noteDamageFrom().");
+					}
+				}
+			}
+		} // end of having the opponent
+	} // end of having any opponent
+}
+
+
+void PLAYER::noteDamageTo(PLAYER* opponent, int32_t damage, bool destroyed)
+{
+	if (opponent) {
+		int32_t idx = 0;
+
+		while ((idx < oppCount) && (opponent != opponents[idx].opponent))
+			++idx;
+
+		if ( idx < oppCount ) {
+			opponents[idx].damage_to += damage;
+			if (destroyed)
+				opponents[idx].killed_them++;
+		}
+	}
+}
+
+
+// if we have some shield strength at the end of the round, then
+// reclaim this shield back into our inventory
+void PLAYER::reclaimShield()
+{
+    if (tank && last_shield_used && (tank->sh > 0))
+		ni[last_shield_used] += 1;
+	last_shield_used = 0;
+}
+
+
+// This function takes one off the player's time to fire.
+// If the player runs out of time, the function returns true.
+// If the player has time left, or no time clock is being used,
+// then the function returns false.
+bool PLAYER::reduceClock()
+{
+	if (! time_left_to_fire)
+		// not using clock
+		return false;
+
+	if (0 == --time_left_to_fire) {
+		time_left_to_fire = env.maxFireTime;
+		return true;
+	}
+
+	return false;
+}
+
+
+/// @brief save game relevant data to @a file
+void PLAYER::save_game_data(FILE* file)
+{
+	fprintf(file, "KILLED=%d\n", killed);
+	fprintf(file, "KILLS=%d\n", kills);
+	fprintf(file, "MONEY=%d\n", money);
+	fprintf(file, "SCORE=%d\n", score);
+	fprintf(file, "TYPE=%d\n", type);
+	fprintf(file, "TYPESAVED=%d\n", type_saved);
+
+	// Preferences, needed for "PERPLAY_PREF" - players
+	if ( (PERPLAY_PREF == preftype) && (HUMAN_PLAYER != type) ) {
+		// Note: "ALWAYS_PREF" - players do not need this here, but in
+		// save_to_file(), as the preferences are generated only once.
+		fprintf(file, "DEFENSIVE=%lf\n", defensive);
+		fprintf(file, "PAINSENSITIVITY=%lf\n", painSensitivity);
+		fprintf(file, "SELFPRESERVATION=%lf\n", selfPreservation);
+		fprintf(file, "VENGEANCETHRESHOLD=%lf\n", vengeanceThreshold);
+		fprintf(file, "VENGEFUL=%d\n", vengeful);
+		for (int32_t i = 0; i < THINGS; ++i)
+			fprintf (file, "WEAPONPREFERENCES=%d %d\n", i, weapPref[i]);
+	}
+
+	// Inventory of the weapons
+	for (int32_t i = 0; i < WEAPONS; ++i)
+		fprintf(file, "WEAPON=%d %d\n", i, nm[i]);
+
+	// Inventory of the items
+	for (int32_t i = 0; i < ITEMS; ++i)
+		fprintf(file, "ITEM=%d %d\n", i, ni[i]);
+
+	// Opponents memory
+	fprintf(file, "OPPCOUNT=%d\n", oppCount);
+	for (int32_t i = 0; i < oppCount; ++i) {
+		int32_t idx = opponents[i].index; // Just a shortcut
+
+		// Save damage from last turn if any is still there:
+		if (opponents[i].damage_last > 0) {
+			opponents[i].damage_from += opponents[i].damage_last;
+			opponents[i].damage_last  = 0;
+		}
+		fprintf(file, "OPPMEM_INDX=%d %d\n",  i, idx);
+		fprintf(file, "OPPMEM_DDEA=%d %d\n",  i, opponents[i].damage_from);
+		fprintf(file, "OPPMEM_DDON=%d %d\n",  i, opponents[i].damage_to);
+		fprintf(file, "OPPMEM_FEAR=%d %lf\n", i, opponents[i].fear);
+		fprintf(file, "OPPMEM_KIME=%d %d\n",  i, opponents[i].killed_me);
+		fprintf(file, "OPPMEM_KITH=%d %d\n",  i, opponents[i].killed_them);
+	}
+
+	fprintf(file, "***\n");
+}
+
+
+/// @brief dump full player data to @a file
+void PLAYER::save_to_file (FILE *file)
+{
+	if (! file)
+		return;
+
+	// start section with "*PLAYER*"
+	fprintf (file, "*PLAYER*\n");
+	fprintf (file, "NAME=%s\n", name); // Set first for easier debugging
+	fprintf (file, "COLOR=%d\n", color);
+	fprintf (file, "DEFENSIVE=%lf\n", defensive);
+	fprintf (file, "PAINSENSITIVITY=%lf\n", painSensitivity);
+	fprintf (file, "PLAYED=%u\n", played);
+	fprintf (file, "PREFTYPE=%d\n", preftype);
+	fprintf (file, "SELFPRESERVATION=%lf\n", selfPreservation);
+	fprintf (file, "TANK_BITMAP=%d\n", tankbitmap);
+	fprintf (file, "TEAM=%d\n", team);
+	fprintf (file, "TYPE=%d\n", type);
+	fprintf (file, "TYPESAVED=%d\n", type_saved);
+	fprintf (file, "VENGEANCETHRESHOLD=%lf\n", vengeanceThreshold);
+	fprintf (file, "VENGEFUL=%d\n", vengeful);
+	fprintf (file, "WON=%u\n", won);
+
+	// Preferences, needed for "ALWAYS_PREF" - players
+	if (ALWAYS_PREF == preftype) {
+		// Note: "PERPLAY_PREF" - players do not need this here, but in
+		// save_game_data(), as the preferences are different in each game.
+		for (int32_t i = 0; i < THINGS; ++i)
+			fprintf (file, "WEAPONPREFERENCES=%d %d\n", i, weapPref[i]);
+	}
+
+	fprintf (file, "***\n");
+}
+
+
+const char* PLAYER::selectGloatPhrase ()
+{
+	return env.gloat->Get_Random_Line();
+}
+
+
+/// @return a constructed panic phrase which must be freed!
+const char *PLAYER::selectPanicPhrase (PLAYER* shocker)
+{
+	if (! shocker)
+		return nullptr;
+
+	const char* line  = env.panic->Get_Random_Line();
+	size_t      tLen  = strlen(shocker->getName()) + strlen(line);
+	char*       pText = (char *)calloc(tLen + 1, sizeof (char));
+
+	if (!pText)
+		return nullptr;
+
+	snprintf(pText, tLen, line, shocker->getName());
+
+	return pText;
+}
+
+
+const char *PLAYER::selectKamikazePhrase ()
+{
+	return env.kamikaze->Get_Random_Line();
+}
+
+
+/// @return a constructed retaliation phrase which must be freed!
+const char *PLAYER::selectRetaliationPhrase ()
+{
+	if (! revenge)
+		return nullptr;
+
+	const char* line  = env.retaliation->Get_Random_Line();
+	const char* rname = revenge->getName();
+	size_t      tLen  = strlen(rname) + 4 + strlen(line);
+	char*       pText = (char *)calloc(tLen + 1, sizeof (char));
+
+	if (pText)
+		atanks_snprintf(pText, tLen, "%s%s !!!", line, rname);
+
+	return pText;
+}
+
+
+const char* PLAYER::selectRevengePhrase ()
+{
+	return env.revenge->Get_Random_Line();
+}
+
+
+const char *PLAYER::selectSuicidePhrase ()
+{
+	return env.suicide->Get_Random_Line();
+}
+
+
+/// @brief store @a last_opp to be remembered as the current/last target
+void PLAYER::setLastOpponent(sOpponent* last_opp)
+{
+	last_opponent = last_opp;
+}
+
+
+void PLAYER::setName (const char* name_)
+{
+	if (!name_ || strncmp(name, name_, NAME_LEN - 1)) {
+		memset(name, 0, NAME_LEN);
+		if (name_)
+			strncpy (name, name_, NAME_LEN - 1);
+	}
+}
+
+
+/// @brief fill in the list of desired items and update their preferences
+void PLAYER::updatePreferences(int32_t max_boost, int32_t max_score)
+{
+	// 1.: Fill cart and preference array.
+	// The preferences are copied, as they might get boosted this round
+	int32_t weapons_in_stock = generateDesiredList();
+	int32_t ai_level         = static_cast<int32_t>(type);
+
+	// 2.: Amplify wish list by current boost and score situation
+	needAmp    = false;
+	needArmour = false;
+	needDamage = false;
+
+	// Check whether boosting armour / amps is wanted:
+	if (getBoostValue() < (max_boost / ai_level)) {
+		// Yes. which ?
+		if (defensive < 0.) {
+			DEBUG_LOG_FIN(name, "updPref: Need to boost amps    (%d / %d)",
+						  getBoostValue(), max_boost / ai_level)
+			needAmp   = true; // Try to come back with more damage output
+		} else {
+			DEBUG_LOG_FIN(name, "updPref: Need to boost armour  (%d / %d)",
+						  getBoostValue(), max_boost / ai_level)
+			needArmour = true; // Try to come back with more endurance
+		}
+	}
+
+	// Fallen behind? Need more weapons?
+	if ( (score <= (max_score / (ai_level + 1)))
+	  && (weapons_in_stock < (2 * ai_level)) ) {
+		DEBUG_LOG_FIN(name, "updPref: Need to boost weapons (%d / %d)",
+					  score, max_score / (ai_level + 1))
+		needDamage = true;
+	}
+
+
+	// 3.: Boost preferences if wanted and lower weapon/item
+	//     preferences if there are enough in stock already.
+	//     Further note down items to sell.
+	boostPrefences(needArmour, needAmp, needDamage);
+
+
+	// 4.: Sort these items by preferences
+	bool isSorted = false;
+	while (!isSorted) {
+		isSorted = true;
+
+		for (int32_t i = 1; i < THINGS; ++i) {
+			int32_t idx_l  = desired[i - 1];
+			int32_t idx_r  = desired[i];
+
+			if ( (currPref[idx_l] < currPref[idx_r])
+				// sort SML_MIS to the back, too
+			  || ( (0 == idx_l) && idx_r) ) {
+				isSorted       = false;
+				desired[i]     = idx_l;
+				desired[i - 1] = idx_r;
+			}
+		}
+	}
+
+#ifdef ATANKS_DEBUG_FINANCE
+	// Get out the top twenty
+	for (int32_t i = 0; i < THINGS; ++i) {
+		DEBUG_LOG_FIN(name, "%2d. preference: %6d - %s",
+					  i + 1, currPref[desired[i]],
+					  desired[i] < WEAPONS
+					  ? weapon[desired[i]].getName()
+					  : item[desired[i] - WEAPONS].getName())
+	}
+#endif // ATANKS_DEBUG_FINANCE
+}
+
+
+/// @brief mini ctor to pacify Visual C++
+PLAYER_mini::PLAYER_mini()
+{
+	memset(name, 0, sizeof(char) * NAME_LEN);
+	strncpy(name, "New Player", NAME_LEN);
+}
+
+
+/// @brief backup a players editable data
+void PLAYER_mini::copy_from(PLAYER* source)
+{
+	if (source) {
+		assert( (source->index > -1) && "INDEX ERROR on PLAYER!");
+		color       = source->color;
+		index       = source->index;
+		strncpy(name, source->getName(), NAME_LEN);
+		played      = source->played;
+		player      = source;
+		preftype    = source->preftype;
+		tankbitmap = source->tankbitmap;
+		team        = source->team;
+		type        = source->type;
+		won         = source->won;
+	}
+}
+
+
+/// @brief copy backed up values back to the source player
+void PLAYER_mini::write_back(PLAYER* target)
+{
+	if (target)
+		player = target;
+	if (player) {
+		player->color = color;
+		player->setName(name);
+		// played is read only.
+		player->preftype = preftype;
+		player->tankbitmap = tankbitmap;
+		player->team = team;
+		player->type = type;
+		// won is read only.
+	}
 }
 
-#endif
 
 
+/// @brief action function to display the edit player screen
+int32_t edit_player(PLAYER** target, int32_t)
+{
+	int32_t result = 0;
+
+	assert(target && "ERROR: target must be set");
+	assert(*target && "ERROR: target must point to something valid!");
+
+	if (!target || !(*target))
+		return -1;
+
+	int32_t menuMid        = 300;
+	int32_t itemLeft       = menuMid - 75;
+	int32_t itemHeight     = env.fontHeight + 2;
+	int32_t itemPadding    = 2;
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t itemY          = itemFullHeight * 3;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+
+
+	// Use "Mini-Player" struct to be able to cancel player editing
+	PLAYER_mini player_bak;
+	player_bak.copy_from(*target);
+
+	// The "Are you sure" screen when deleting a player
+	Menu areyousure(MC_AREYOUSURE,
+					env.halfWidth - menuMid, env.menuBeginY);
+	areyousure.addButton( 1, nullptr, PE_CONFIRM_DEL,
+						env.misc[7], nullptr, env.misc[8], false,
+						menuMid + 50,
+						menuHeight- btnHeight - 6, 0, 0, itemPadding);
+	areyousure.addButton( 2, nullptr, PE_BACK,
+						env.misc[7], nullptr, env.misc[8], false,
+						menuMid - env.misc[7]->w - 50,
+						menuHeight- btnHeight - 6, 0, 0, itemPadding);
+
+	// The menu, but with the player name as title
+	Menu menu(MC_PLAYER, env.halfWidth - menuMid, env.menuBeginY);
+	menu.setTitle(player_bak.name, false);
+
+	// "Name"
+	menu.addText(player_bak.name, 1, NAME_LEN, player_bak.color, "%s",
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Colour"
+	menu.addColor(&player_bak.color, 2, itemLeft, itemY, 150, 50, 25, itemPadding);
+	itemY += 50 + itemPadding;
+
+	// "Type"
+	menu.addValue(&player_bak.type, 3, nullptr, BLACK,
+				TC_PLAYERTYPE, static_cast<int32_t>(DEADLY_PLAYER),
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Team"
+	menu.addValue(&player_bak.team, 4, nullptr, BLACK,
+				TC_PLAYERTEAM, static_cast<int32_t>(TEAM_JEDI),
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Generate Pref"
+	menu.addValue(&player_bak.preftype, 5, nullptr, BLACK,
+				TC_PLAYERPREF, static_cast<int32_t>(ALWAYS_PREF),
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Played"
+	menu.addText(&player_bak.played, 6, BLACK, "% 8u",
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Won"
+	menu.addText(&player_bak.won, 7, BLACK, "% 8u",
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Tank Type"
+	menu.addValue(&player_bak.tankbitmap, 8, nullptr, BLACK,
+				TC_TANKTYPE, static_cast<int32_t>(TT_MINI),
+				itemLeft, itemY, 150, 35, itemPadding,
+				display_tank_desc);
+	itemY += 35 + itemPadding;
+
+	// "Delete This Player"
+	menu.addMenu(&areyousure, 9, RED, itemLeft, itemY, 150, itemFullHeight, itemPadding);
+
+	// "Okay" and "Back"
+	menu.addButton(10, nullptr, PE_CONFIRM_EDIT, env.misc[7], nullptr,
+						env.misc[8], false,
+						menuMid + 50,
+						menuHeight- btnHeight - 6, 0, 0, itemPadding);
+	menu.addButton(11, nullptr, PE_BACK, env.misc[7], nullptr,
+						env.misc[8], false,
+						menuMid - env.misc[7]->w - 50,
+						menuHeight- btnHeight - 6, 0, 0, itemPadding);
+
+	result = menu();
+
+	// If the editing is confirmed, the backup must be written back
+	if (PE_CONFIRM_EDIT & result)
+		player_bak.write_back();
+
+	// If the player shall be deleted, the player index must be added
+	if (PE_CONFIRM_DEL & result)
+		result |= player_bak.index;
+
+	return result;
+}
+
+static PLAYER_mini player_new; //!< Used by new_player to keep previous settings
 
+/// @brief action function to display the edit player screen
+int32_t new_player(PLAYER** target, int32_t)
+{
+	int32_t result = 0;
+
+	assert(target && "ERROR: target must be set");
+	assert((nullptr == *target)
+		  && "ERROR: *target must nullptr!");
+
+	if (!target || *target)
+		return -1;
+
+	int32_t menuMid        = 300;
+	int32_t itemLeft       = menuMid - 75;
+	int32_t itemHeight     = env.fontHeight + 2;
+	int32_t itemPadding    = 2;
+	int32_t itemFullHeight = itemHeight + itemPadding;
+	int32_t itemY          = itemFullHeight * 3;
+	int32_t btnHeight      = env.misc[7]->h + itemPadding;
+	int32_t menuHeight     = env.menuEndY - env.menuBeginY; // Raw height
+
+	// The menu, with title from the menu class
+	Menu menu(MC_PLAYER, env.halfWidth - menuMid, env.menuBeginY);
+
+	// "Name"
+	menu.addText(player_new.name, 1, NAME_LEN, player_new.color, "%s",
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Colour"
+	menu.addColor(&player_new.color, 2, itemLeft, itemY, 150, 50, 25, itemPadding);
+	itemY += 50 + itemPadding;
+
+	// "Type"
+	menu.addValue(&player_new.type, 3, nullptr, BLACK,
+				TC_PLAYERTYPE, static_cast<int32_t>(DEADLY_PLAYER),
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Team"
+	menu.addValue(&player_new.team, 4, nullptr, BLACK,
+				TC_PLAYERTEAM, static_cast<int32_t>(TEAM_JEDI),
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Generate Pref"
+	menu.addValue(&player_new.preftype, 5, nullptr, BLACK,
+				TC_PLAYERPREF, static_cast<int32_t>(ALWAYS_PREF),
+				itemLeft, itemY, 150, itemHeight, itemPadding);
+	itemY += itemFullHeight;
+
+	// "Played" and "Won" do not make sense here
+
+	// "Tank Type"
+	menu.addValue(&player_new.tankbitmap, 8, nullptr, BLACK,
+				TC_TANKTYPE, static_cast<int32_t>(TT_MINI),
+				itemLeft, itemY, 150, 35, itemPadding,
+				display_tank_desc);
+	itemY += 35 + itemPadding;
+
+	// "Delete This Player" is surely not needed
+
+	// "Okay" and "Back"
+	menu.addButton(10, nullptr, PE_CONFIRM_NEW, env.misc[7], nullptr,
+						env.misc[8], false,
+						menuMid + 50,
+						menuHeight- btnHeight - 6, 0, 0, itemPadding);
+	menu.addButton(11, nullptr, PE_BACK, env.misc[7], nullptr,
+						env.misc[8], false,
+						menuMid - env.misc[7]->w - 50,
+						menuHeight- btnHeight - 6, 0, 0, itemPadding);
+
+	while (!result) {
+		char existsMessage[200];
+		result = menu();
+
+		// If the player is to be created, two things must happen.
+		// First, ensure that the name is unique
+		// Second, create the real player
+		if (PE_CONFIRM_NEW & result) {
+            if (-1 == env.getPlayerByName(player_new.name)) {
+				*target = env.createNewPlayer(player_new.name);
+				if (*target)
+					player_new.write_back(*target);
+            } else {
+				snprintf(existsMessage, 199, "The player \"%s\" already exists!",
+						player_new.name);
+				errorMessage = existsMessage;
+				errorX = env.halfWidth - text_length(font, errorMessage) / 2;
+				errorY = env.menuBeginY + itemFullHeight;
+				result = 0;
+            }
+		}
+	} // End of !result
 
+	return result;
+}
diff --git a/src/player.h b/src/player.h
index cb82708..216fe67 100644
--- a/src/player.h
+++ b/src/player.h
@@ -18,190 +18,240 @@
  * 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.
- * */
+ *
+ */
 
 
-#include "main.h"
-#include "menu.h"
+#include "player_types.h"
+#include "globaltypes.h"
 
+#define	MAX_WEAP_PROBABILITY 10000
+#define BURIED_LEVEL         135
+#define BURIED_LEVEL_HALF     68
 
-enum playerType
-{
-  HUMAN_PLAYER,
-  USELESS_PLAYER,
-  GUESSER_PLAYER,
-  RANGEFINDER_PLAYER,
-  TARGETTER_PLAYER,
-  DEADLY_PLAYER,
-  LAST_PLAYER_TYPE,
-  PART_TIME_BOT,         // normally a human, but acting as a deadly computer
-  VERY_PART_TIME_BOT,    // just fires one shot
-  NETWORK_CLIENT
-};
-//player weapon preference type
-//ALWAYS_PREF - only choose weapon preferences once on player creation
-//PERPLAY_PREF - choose weapon preferences once per game
-enum playerPrefType
+#define NET_COMMAND_SIZE 64
+// if we do not get a command after this amount of seconds,
+// turn control over to the computer for a moment
+#define NET_DELAY 1000
+#define NET_DELAY_SHORT 500
+
+class TANK;
+class PLAYER;
+class AICore;
+
+/// @brief minimal struct to allow AI players to keep track of friend and foe.
+struct sOpponent
 {
-  PERPLAY_PREF,
-  ALWAYS_PREF
+	int32_t damage_from = 0;  //!< How much damage the opponent did to the player.
+	int32_t damage_last = 0;  //!< How much damage the opponent did in this turn.
+	int32_t damage_to   = 0;  //!< How much damage the player did to the opponent.
+	double  fear        = 0.; //!< How likely evasive manoeuvres are started.
+	double  fear_shock  = 0.; //!< The highest shock value determines the shocker.
+	int32_t index       = -1; //!< Needed for saving/loading to work.
+	int32_t killed_me   = 0;  //!< How many times this opponent has killed this player.
+	int32_t killed_them = 0;  //!< How many times this opponent was killed by this player.
+	PLAYER* opponent    = nullptr; //!< The PLAYER memorized here.
+	int32_t revenge_dmg = 0;  //!< Summed up damage to determine when it is time for revenge.
 };
-enum turnStages
+
+
+/** @class PLAYER
+  * @brief All data concerning human and A players
+**/
+class PLAYER
 {
-  SELECT_WEAPON,
-  SELECT_TARGET,
-  CALCULATE_ATTACK,
-  AIM_WEAPON,
-  FIRE_WEAPON
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit PLAYER ();
+	~PLAYER	();
+
+	// no copying, no assignments
+	PLAYER(const PLAYER&) =delete;
+	PLAYER &operator=(const PLAYER&) =delete;
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void        checkOppMem         ();
+	int32_t     chooseItemToBuy     (int32_t max_boost, int32_t &last_idx);
+	eControl    controlTank         (AICore* aicore, bool allow_fire);
+	void        drawIndicator       (int32_t x, int32_t y, int32_t h);
+#ifdef NETWORK
+	eControl    executeNetCmd       (bool my_turn, AICore* aicore);
+#endif // NETWORK
+	void	    exitShop            ();
+	void        generatePreferences ();
+	int32_t     getBoostValue       ();
+	int32_t     getItemPref         (int32_t idx);
+	int32_t     getMoneyToSave      (bool first_look);
+	const char* getName             () const;
+	bool        getNetCmd           ();
+	sOpponent*  getOppMem           (int32_t idx);
+	const char* getTeamName         () const;
+	int32_t     getWeapPref         (int32_t idx);
+	void	    initialise          (bool loaded_game);
+	bool        load_from_file      (FILE* file);
+	void        load_game_data      (FILE* file);
+	void        newGame             ();
+	void	    newRound            ();
+	void        noteDamageFrom      (PLAYER* opponent, int32_t damage, bool destroyed);
+	void        noteDamageTo        (PLAYER* opponent, int32_t damage, bool destroyed);
+	void        reclaimShield       ();    // restore unused shield
+	bool        reduceClock         ();
+	void        save_game_data      (FILE* file);
+	void        save_to_file        (FILE* file);
+	const char* selectGloatPhrase   ();
+	const char* selectPanicPhrase   (PLAYER* shocker);
+	const char* selectKamikazePhrase();
+	const char* selectRetaliationPhrase();
+	const char* selectRevengePhrase ();
+	const char* selectSuicidePhrase ();
+	void        setLastOpponent     (sOpponent* last_opp);
+	void	    setName             (const char* name_);
+	void        updatePreferences   (int32_t max_boost, int32_t max_score);
+
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+	int32_t        color              = BLACK;
+	double         damageMultiplier   = 1.;
+	double         defensive          = 0.; // [-1.0;1.0], offensive - defensive
+	double         errorMultiplier    = 0.;
+	bool           changed_weapon     = false;
+	double         focusRate          = 0.;
+	bool           gloating           = false;
+	int32_t        index              = -1; // To note where in allPlayers this player is saved
+	int32_t        killed             = 0;
+	int32_t        kills              = 0;
+	int32_t        last_shield_used   = 0;
+	double         painSensitivity    = .5; // How sensitive to damage
+	uint32_t       played             = 0;
+	playerPrefType preftype           = PERPLAY_PREF;
+	playerType     previous_type      = HUMAN_PLAYER;
+	int32_t        money              = 15000;
+	int32_t        ni[ITEMS];
+	int32_t        nm[WEAPONS];
+	PLAYER*        revenge            = nullptr;
+	int32_t        score              = 0;
+	abool_t        sdi_has_fired;           // Only one shot per frame
+	int32_t        sdiShots           = 0;
+	bool           selected           = false;
+	double         selfPreservation   = .5; // Lengths gone to to avoid self-harm
+	bool           skip_me            = false;
+	TANK*          tank               = nullptr;
+	int32_t        tankbitmap         = TT_NORMAL;
+	eTeamTypes     team               = TEAM_NEUTRAL;
+	int32_t        time_left_to_fire  = 0;
+	playerType     type               = HUMAN_PLAYER;
+	playerType     type_saved         = HUMAN_PLAYER;
+	double         vengeanceThreshold = .5; // Damage required to warrant revenge
+	int32_t        vengeful           = 50; // 0-100 chance of retaliation
+	uint32_t       won                = 0;
+#ifdef NETWORK
+	int32_t        server_socket      = 0;
+	char           net_command[NET_COMMAND_SIZE] = { 0 };
+#endif // NETWORK
+
+
+private:
+
+	typedef ePlayerStages plStage_t;
+	typedef sOpponent     opp_t;
+
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void        boostPrefences           (bool boostArmour, bool boostAmps,
+	                                      bool boostWeapons);
+	bool        buy_item                 (int32_t itemindex, int32_t max_boost);
+	eControl    computerControls         (AICore *aicore, bool allow_fire);
+	int32_t     computerSelectPreBuyItem (int32_t max_boost);
+	int32_t     generateDesiredList      ();
+	int32_t     getAmpValue              ();
+	int32_t     getArmourValue           ();
+	eControl    humanControls            (AICore* aicore);
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	int32_t   boostBought      = -1;
+	int32_t   currPref[THINGS];     // current preferences, calculated for each round
+	int32_t   desired[THINGS];      // Shopping wish list
+	int32_t   saveMoneyFor[THINGS]; // List of items the AI wants to buy
+	opp_t*    last_opponent    = nullptr;
+	char      name[NAME_LEN + 1];
+	bool      needAmp          = false;
+	bool      needArmour       = false;
+	bool      needDamage       = false;
+	int32_t   oppCount         = 0;
+	opp_t*    opponents        = nullptr;
+	plStage_t plStage          = PS_SELECT_WEAPON;
+	int32_t   shieldBought     = -1;
+	int32_t   weapPref[THINGS]; // Static preferences, generated once
 };
 
-#define TANK_TYPES 9
 
-#define	NUM_ROULETTE_SLOTS (THINGS * 100)
-#define	MAX_WEAP_PROBABILITY	10000
-#define BURIED_LEVEL 135
+// For headers including player.h to know that the class is there:
+#define HAS_PLAYER 1
+// Note: Due to circular dependencies, some headers might need to forward
+//       the player class.
 
-#define NET_COMMAND_SIZE 64
-// if we do not get a command after this amount of seconds,
-// turn control over to the computer for a moment
-#define NET_DELAY 1000
-#define NET_DELAY_SHORT 500
 
+/** @struct PLAYER_mini
+  * @brief Minimum dataset of editable values.
+  *
+  * This minimal struct is used for the player editing menu.
+  *   - When adding a new player it allows to cancel the addition without
+  * atanks to first call new and then delete. Further it keeps the last
+  * settings so adding many new players with the same settings but different
+  * names becomes very easy.
+ **/
+struct PLAYER_mini
+{
+	int32_t        color          = GREEN;
+	int32_t        index          = -1;
+	char	       name[NAME_LEN];
+	uint32_t       played         = 0;
+	PLAYER*        player         = nullptr;
+	playerPrefType preftype       = ALWAYS_PREF;
+	int32_t        tankbitmap     = TT_NORMAL;
+	eTeamTypes     team           = TEAM_NEUTRAL;
+	playerType     type           = HUMAN_PLAYER;
+	uint32_t       won            = 0;
 
-// values the control functions return ot the main game loop
-#define CONTROL_PRESSED 2   // some key pressed, but not to shoot
-#define CONTROL_FIRE 1
-#define CONTROL_QUIT -1
-#define CONTROL_SKIP -2
+	// a ctor, needed by VisualC++ for the name.
+	explicit PLAYER_mini();
 
+	// "Backup a player"
+	void copy_from(PLAYER* source);
+
+	// Write back the values
+	void write_back(PLAYER* target = nullptr);
+};
+
+#define HAS_PLAYER_MINI 1
+
+// Helper functions to be used as action function with ET_BUTTON entries
+int32_t edit_player(PLAYER** target, int32_t);
+int32_t new_player (PLAYER** target, int32_t);
 
-class TANK;
-class PLAYER
-  {
-
-    char	_name[NAME_LENGTH];
-    int	_currTank;
-    GLOBALDATA *_global;
-    ENVIRONMENT *_env;
-    char	_turnStage;
-    TANK	*_target;
-    TANK  *_oldTarget;
-    int	_targetAngle;
-    int	_targetPower;
-    int	_overshoot;
-    int	_weaponPreference[THINGS];
-    int	_rouletteWheel[NUM_ROULETTE_SLOTS];
-    int	_targetX;
-    int	_targetY;
-    int iBoostItemsBought;
-    int iTargettingRound;
-    int computerSelectPreBuyItem (int aMaxBoostValue = 0);
-    TANK * computerSelectTarget (int aPreferredWeapon, bool aRotationMode = false);
-    char * selectKamikazePhrase();
-    char * selectRetaliationPhrase();
-    int getBlastValue (TANK * aTarget, int aDamage, int aWeapon, double aDamageMod = 1.0);
-    int getUnburyingTool();
-    int adjustOvershoot(int &aOvershoot, double aReachedX, double aReachedY);
-    int getAdjustedTargetX(TANK * aTarget = NULL);
-    int calculateAttack(TANK *aTarget = NULL);
-    void calculateAttackValues(int &aDistanceX, int aDistanceY, int &aRawAngle, int &aAdjAngle, int &aPower, bool aAllowFlip);
-    int	traceShellTrajectory (double aTargetX, double aTargetY, double aVelocityX, double aVelocityY, double &aReachedX, double &aReachedY);
-    int	rangeFind (int &aAngle, int &aPower);
-
-  public:
-    double	type, type_saved, previous_type;
-    double preftype;
-    char* preftypeText[ALWAYS_PREF + 1];
-    char *tank_type[8];
-    char *teamText[3];
-
-    PLAYER	*revenge;
-    int	vengeful;	// 0-100 chance of retaliation
-    double	vengeanceThreshold;	// Damage required to warrant revenge
-    //   (% of max armour)
-    double	defensive;	// -1.0 - 1.0, offense - defense
-    double	annoyanceFactor;
-    double	selfPreservation;	// Lengths gone to to avoid self-harm
-    double	painSensitivity;	// How sensitive to damage
-    int	rangeFindAttempts;
-    int	retargetAttempts;
-    double	focusRate;
-    double	errorMultiplier;
-    int	score;
-    double	played, won;
-    int     kills, killed;
-    double	selected;
-    int	money;
-    double	damageMultiplier;
-    TANK	*tank;
-    int	color;
-    void *pColor;
-    int	color2;
-    int 	nm[WEAPONS], ni[ITEMS];
-    MENUENTRY	*menuopts;
-    MENUDESC	*menudesc;
-    char *typeText[LAST_PLAYER_TYPE];
-    bool changed_weapon;
-    bool gloating;
-    double tank_bitmap;      // which type of tank do we have
-    double team;
-    int time_left_to_fire;
-    bool skip_me;
-    int last_shield_used;
-    #ifdef NETWORK
-    int server_socket;
-    char net_command[NET_COMMAND_SIZE];
-    #endif
-
-    PLAYER (GLOBALDATA *global, ENVIRONMENT *env);
-    // PLAYER (GLOBALDATA *global, ENVIRONMENT *env, ifstream &ifsFile, bool file); 
-    ~PLAYER	();
-    void	setName (char *name);
-    char	*getName ()
-    {
-      return (_name);
-    };
-    int	calculateDirectAngle (int dx, int dy);
-    void	exitShop ();
-    void	newRound ();
-    void	initialise ();
-    TANK	*nextTank ();
-    TANK	*currTank ();
-    int	controlTank ();
-    int	humanControls ();
-    int	computerControls ();
-    double	calcDefenseValue (TANK *ctank, TANK *ltank);
-    double	selectTarget (); // select x to aim for
-    double	selectTarget (int *targetXCoord, int *targetYCoord); // select x to aim for
-    double  Select_Target (int *target_X, int *target_Y); // select x to aim for
-    int	computerSelectItem (); // Choose weapon to fire
-    int	chooseItemToBuy (int aMaxBoostValue = 0);
-    void	generatePreferences ();
-    void	setComputerValues (int aOffset = 0);
-    int getMoneyToSave();
-    int getBoostValue();
-    int	selectRandomItem ();
-    char	*selectRevengePhrase ();
-    char	*selectGloatPhrase ();
-    char    *selectSuicidePhrase ();
-    int     saveToFile_Text (FILE *file);
-    int     saveToFile (ofstream &ofsFile);
-    int     loadFromFile_Text (FILE *file);
-    int     loadFromFile (ifstream &ifsFile);
-    void	initMenuDesc ();
-    char    *Get_Team_Name();
-    int     Select_Random_Weapon();
-    int     Select_Random_Item();
-    int    Reduce_Time_Clock();
-    int    Buy_Something(int item_index);     // purchases the selected item
-    int    Reclaim_Shield();    // restore unused shield
-
-    #ifdef NETWORK
-    void Trim_Newline(char *line);   // sanitize input
-    int Get_Network_Command();
-    int Execute_Network_Command(int my_turn);
-    #endif
-  };
 
 #endif
 
diff --git a/src/player_types.cpp b/src/player_types.cpp
new file mode 100644
index 0000000..7c45767
--- /dev/null
+++ b/src/player_types.cpp
@@ -0,0 +1,150 @@
+#include "player_types.h"
+
+playerType &operator+=(playerType &src, int32_t val)
+{
+	int32_t cur = static_cast<int32_t>(src) + val;
+	if (cur > 0)
+		cur %= LAST_PLAYER_TYPE;
+	if (cur < 0)
+		cur = LAST_PLAYER_TYPE - ((-1 * cur) % LAST_PLAYER_TYPE);
+	src = static_cast<playerType>(cur);
+	return src;
+}
+
+playerType &operator-=(playerType &src, int32_t val)
+{
+	return src += -1 * val;
+}
+
+playerType &operator++(playerType &src)
+{
+	return src += 1;
+}
+
+playerType operator++(playerType &src, int32_t)
+{
+	playerType old_val = src;
+	src += 1;
+	return old_val;
+}
+
+
+
+playerPrefType &operator+=(playerPrefType &src, int32_t val)
+{
+	int32_t cur = static_cast<int32_t>(src) + val;
+	if (cur > 0)
+		cur %= PREF_COUNT;
+	if (cur < 0)
+		cur = PREF_COUNT - ((-1 * cur) % PREF_COUNT);
+	src = static_cast<playerPrefType>(cur);
+	return src;
+}
+
+playerPrefType &operator-=(playerPrefType &src, int32_t val)
+{
+	return src += -1 * val;
+}
+
+playerPrefType &operator++(playerPrefType &src)
+{
+	return src += 1;
+}
+
+playerPrefType  operator++(playerPrefType &src, int32_t)
+{
+	playerPrefType old_val = src;
+	src += 1;
+	return old_val;
+}
+
+
+
+ePlayerStages &operator+=(ePlayerStages &src, int32_t val)
+{
+	int32_t cur = static_cast<int32_t>(src) + val;
+	if (cur > 0)
+		cur %= PS_STAGE_COUNT;
+	if (cur < 0)
+		cur = PS_STAGE_COUNT - ((-1 * cur) % PS_STAGE_COUNT);
+	src = static_cast<ePlayerStages>(cur);
+	return src;
+}
+
+ePlayerStages &operator-=(ePlayerStages &src, int32_t val)
+{
+	return src += -1 * val;
+}
+
+ePlayerStages &operator++(ePlayerStages &src)
+{
+	return src += 1;
+}
+
+ePlayerStages  operator++(ePlayerStages &src, int32_t)
+{
+	ePlayerStages old_val = src;
+	src += 1;
+	return old_val;
+}
+
+
+
+eTeamTypes &operator+=(eTeamTypes &src, int32_t val)
+{
+	int32_t cur = static_cast<int32_t>(src) + val;
+	if (cur > 0)
+		cur %= TEAM_COUNT;
+	if (cur < 0)
+		cur = TEAM_COUNT - ((-1 * cur) % TEAM_COUNT);
+	src = static_cast<eTeamTypes>(cur);
+	return src;
+}
+
+eTeamTypes &operator-=(eTeamTypes &src, int32_t val)
+{
+	return src += -1 * val;
+}
+
+eTeamTypes &operator++(eTeamTypes &src)
+{
+	return src += 1;
+}
+
+eTeamTypes  operator++(eTeamTypes &src, int32_t)
+{
+	eTeamTypes old_val = src;
+	src += 1;
+	return old_val;
+}
+
+
+
+eTankTypes &operator+=(eTankTypes &src, int32_t val)
+{
+	int32_t cur = static_cast<int32_t>(src) + val;
+	if (cur > 0)
+		cur %= TT_TANK_COUNT;
+	if (cur < 0)
+		cur = TT_TANK_COUNT - ((-1 * cur) % TT_TANK_COUNT);
+	src = static_cast<eTankTypes>(cur);
+	return src;
+}
+
+eTankTypes &operator-=(eTankTypes &src, int32_t val)
+{
+	return src += -1 * val;
+}
+
+eTankTypes &operator++(eTankTypes &src)
+{
+	return src += 1;
+}
+
+eTankTypes  operator++(eTankTypes &src, int32_t)
+{
+	eTankTypes old_val = src;
+	src += 1;
+	return old_val;
+}
+
diff --git a/src/player_types.h b/src/player_types.h
new file mode 100644
index 0000000..0f54f1e
--- /dev/null
+++ b/src/player_types.h
@@ -0,0 +1,130 @@
+#pragma once
+#ifndef ATANKS_SRC_PLAYER_TYPES_H_INCLUDED
+#define ATANKS_SRC_PLAYER_TYPES_H_INCLUDED
+
+/** @file player_types.h
+  * @brief used enums plus operators for players and tanks
+**/
+
+#include "main.h"
+
+enum ePlayerStages
+{
+	PS_AI_IS_IDLE = 0, //!< AI has nothing to do and is free to get work
+	PS_AI_INITIALIZE,  //!< AI is initializing its data
+	PS_SELECT_TARGET,  //!< AI is selecting a target
+	PS_SELECT_WEAPON,  //!< AI is selecting a weapon or item
+	PS_CALCULATE,      //!< AI calculates its basic attack values
+	PS_AIM,            //!< AI aims the current selection to hit its target
+	PS_FIRE,           //!< AI is ready to have the current weapon/item fired
+	PS_CLEANUP,        //!< AI is cleaning up
+	PS_STAGE_COUNT
+};
+
+ePlayerStages &operator+=(ePlayerStages &src, int32_t val);
+ePlayerStages &operator-=(ePlayerStages &src, int32_t val);
+ePlayerStages &operator++(ePlayerStages &src);          // pre
+ePlayerStages  operator++(ePlayerStages &src, int32_t); // post
+
+enum playerType
+{
+	HUMAN_PLAYER = 0,
+	USELESS_PLAYER,
+	GUESSER_PLAYER,
+	RANGEFINDER_PLAYER,
+	TARGETTER_PLAYER,
+	DEADLY_PLAYER,
+	LAST_PLAYER_TYPE,
+	PART_TIME_BOT,         // normally a human, but acting as a deadly computer
+	VERY_PART_TIME_BOT,    // just fires one shot
+	NETWORK_CLIENT,
+	SDI_PREDICTOR          // Used so missile mind shots from the SDI won't
+	                       // trigger another SDI check, trigger another SDI
+	                       // check, trigger another...
+};
+
+playerType &operator+=(playerType &src, int32_t val);
+playerType &operator-=(playerType &src, int32_t val);
+playerType &operator++(playerType &src);          // pre
+playerType  operator++(playerType &src, int32_t); // post
+
+// player weapon preference type
+// ALWAYS_PREF - only choose weapon preferences once on player creation
+// PERPLAY_PREF - choose weapon preferences once per game
+enum playerPrefType
+{
+	PERPLAY_PREF = 0,
+	ALWAYS_PREF,
+	PREF_COUNT
+};
+
+playerPrefType &operator+=(playerPrefType &src, int32_t val);
+playerPrefType &operator-=(playerPrefType &src, int32_t val);
+playerPrefType &operator++(playerPrefType &src);          // pre
+playerPrefType  operator++(playerPrefType &src, int32_t); // post
+
+
+/** @enum ePlayerEdit
+  * @brief return codes used by the sub menus when editing players
+**/
+enum ePlayerEdit
+{
+	PE_BACK         = 1,        //!< User opted out. No new player, no edit and no deletion.
+	PE_CONFIRM_NEW  = 0x010000, //!< Adding a new player was confirmed
+	PE_CONFIRM_EDIT = 0x020000, //!< Changes to a player have been confirmed
+	PE_CONFIRM_DEL  = 0x040000  //!< Deleting a player was confirmed
+	// Note: The values allow to use the last 16 bit for key code bit masks.
+};
+
+/** @enum eTeamTypes
+  * @brief determines the team a player belongs to
+**/
+enum eTeamTypes
+{
+	TEAM_SITH    = 0,
+	TEAM_NEUTRAL,
+	TEAM_JEDI,
+	TEAM_COUNT
+};
+
+eTeamTypes &operator+=(eTeamTypes &src, int32_t val);
+eTeamTypes &operator-=(eTeamTypes &src, int32_t val);
+eTeamTypes &operator++(eTeamTypes &src);          // pre
+eTeamTypes  operator++(eTeamTypes &src, int32_t); // post
+
+/** @enum eTankOffsets
+  * @brief Centrally store the bitmap offsets of the tank images
+**/
+enum eTankOffsets
+{
+	TO_TURRET = 0,
+	TO_TANK   = 7
+};
+
+
+/** @enum eTankTypes
+  * @brief the tanks currently known, TT_TANK_COUNT is the number of tanks
+**/
+enum eTankTypes
+{
+	TT_NORMAL = 0,
+	TT_CLASSIC,
+	TT_BIGGREY,
+	TT_T34,
+	TT_HEAVY,
+	TT_FUTURE,
+	TT_UFO,
+	TT_SPIDER,
+	TT_BIGFOOT,
+	TT_MINI,
+	TT_TANK_COUNT
+};
+
+eTankTypes &operator+=(eTankTypes &src, int32_t val);
+eTankTypes &operator-=(eTankTypes &src, int32_t val);
+eTankTypes &operator++(eTankTypes &src);          // pre
+eTankTypes  operator++(eTankTypes &src, int32_t); // post
+
+
+#endif // ATANKS_SRC_PLAYER_TYPES_H_INCLUDED
+
diff --git a/src/resource.h b/src/resource.h
new file mode 100755
index 0000000..fb1b95b
--- /dev/null
+++ b/src/resource.h
@@ -0,0 +1,1569 @@
+//{{NO_DEPENDENCIES}}
+// Von Microsoft Visual C++ generierte Includedatei.
+// Verwendet durch atanks.rc
+//
+#define SW_HIDE                         0
+#define HIDE_WINDOW                     0
+#define WM_NULL                         0x0000
+#define WA_INACTIVE                     0
+#define HTNOWHERE                       0
+#define SMTO_NORMAL                     0x0000
+#define ICON_SMALL                      0
+#define SIZE_RESTORED                   0
+#define BN_CLICKED                      0
+#define BST_UNCHECKED                   0x0000
+#define HDS_HORZ                        0x0000
+#define TBSTYLE_BUTTON                  0x0000
+#define TBS_HORZ                        0x0000
+#define TBS_BOTTOM                      0x0000
+#define TBS_RIGHT                       0x0000
+#define LVS_ICON                        0x0000
+#define LVS_ALIGNTOP                    0x0000
+#define TCS_TABS                        0x0000
+#define TCS_SINGLELINE                  0x0000
+#define TCS_RIGHTJUSTIFY                0x0000
+#define DTS_SHORTDATEFORMAT             0x0000
+#define PGS_VERT                        0x00000000
+#define LANG_NEUTRAL                    0x00
+#define SUBLANG_NEUTRAL                 0x00
+#define SORT_DEFAULT                    0x0
+#define SORT_JAPANESE_XJIS              0x0
+#define SORT_CHINESE_BIG5               0x0
+#define SORT_CHINESE_PRCP               0x0
+#define SORT_KOREAN_KSC                 0x0
+#define SORT_HUNGARIAN_DEFAULT          0x0
+#define SORT_GEORGIAN_TRADITIONAL       0x0
+#define _USE_DECLSPECS_FOR_SAL          0
+#define _USE_ATTRIBUTES_FOR_SAL         0
+#define __drv_typeConst                 0
+#define WINAPI_PARTITION_APP            1
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+#define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID 1
+#define SW_SHOWNORMAL                   1
+#define SW_NORMAL                       1
+#define SHOW_OPENWINDOW                 1
+#define SW_PARENTCLOSING                1
+#define VK_LBUTTON                      0x01
+#define WM_CREATE                       0x0001
+#define WA_ACTIVE                       1
+#define PWR_OK                          1
+#define PWR_SUSPENDREQUEST              1
+#define NFR_ANSI                        1
+#define UIS_SET                         1
+#define UISF_HIDEFOCUS                  0x1
+#define XBUTTON1                        0x0001
+#define WMSZ_LEFT                       1
+#define HTCLIENT                        1
+#define SMTO_BLOCK                      0x0001
+#define MA_ACTIVATE                     1
+#define ICON_BIG                        1
+#define SIZE_MINIMIZED                  1
+#define MK_LBUTTON                      0x0001
+#define TME_HOVER                       0x00000001
+#define CS_VREDRAW                      0x0001
+#define CF_TEXT                         1
+#define SCF_ISSECURE                    0x00000001
+#define IDOK                            1
+#define BN_PAINT                        1
+#define BST_CHECKED                     0x0001
+#define TBSTYLE_SEP                     0x0001
+#define TTS_ALWAYSTIP                   0x01
+#define TBS_AUTOTICKS                   0x0001
+#define UDS_WRAP                        0x0001
+#define PBS_SMOOTH                      0x01
+#define LWS_TRANSPARENT                 0x0001
+#define LVS_REPORT                      0x0001
+#define TVS_HASBUTTONS                  0x0001
+#define TVS_EX_NOSINGLECOLLAPSE         0x0001
+#define TCS_SCROLLOPPOSITE              0x0001
+#define ACS_CENTER                      0x0001
+#define MCS_DAYSTATE                    0x0001
+#define DTS_UPDOWN                      0x0001
+#define PGS_HORZ                        0x00000001
+#define NFS_EDIT                        0x0001
+#define BCSIF_GLYPH                     0x0001
+#define BCSS_NOSPLIT                    0x0001
+#define LANG_ARABIC                     0x01
+#define SUBLANG_DEFAULT                 0x01
+#define SUBLANG_AFRIKAANS_SOUTH_AFRICA  0x01
+#define SUBLANG_ALBANIAN_ALBANIA        0x01
+#define SUBLANG_ALSATIAN_FRANCE         0x01
+#define SUBLANG_AMHARIC_ETHIOPIA        0x01
+#define SUBLANG_ARABIC_SAUDI_ARABIA     0x01
+#define SUBLANG_ARMENIAN_ARMENIA        0x01
+#define SUBLANG_ASSAMESE_INDIA          0x01
+#define SUBLANG_AZERI_LATIN             0x01
+#define SUBLANG_AZERBAIJANI_AZERBAIJAN_LATIN 0x01
+#define SUBLANG_BANGLA_INDIA            0x01
+#define SUBLANG_BASHKIR_RUSSIA          0x01
+#define SUBLANG_BASQUE_BASQUE           0x01
+#define SUBLANG_BELARUSIAN_BELARUS      0x01
+#define SUBLANG_BENGALI_INDIA           0x01
+#define SUBLANG_BRETON_FRANCE           0x01
+#define SUBLANG_BULGARIAN_BULGARIA      0x01
+#define SUBLANG_CATALAN_CATALAN         0x01
+#define SUBLANG_CENTRAL_KURDISH_IRAQ    0x01
+#define SUBLANG_CHEROKEE_CHEROKEE       0x01
+#define SUBLANG_CHINESE_TRADITIONAL     0x01
+#define SUBLANG_CORSICAN_FRANCE         0x01
+#define SUBLANG_CZECH_CZECH_REPUBLIC    0x01
+#define SUBLANG_CROATIAN_CROATIA        0x01
+#define SUBLANG_DANISH_DENMARK          0x01
+#define SUBLANG_DARI_AFGHANISTAN        0x01
+#define SUBLANG_DIVEHI_MALDIVES         0x01
+#define SUBLANG_DUTCH                   0x01
+#define SUBLANG_ENGLISH_US              0x01
+#define SUBLANG_ESTONIAN_ESTONIA        0x01
+#define SUBLANG_FAEROESE_FAROE_ISLANDS  0x01
+#define SUBLANG_FILIPINO_PHILIPPINES    0x01
+#define SUBLANG_FINNISH_FINLAND         0x01
+#define SUBLANG_FRENCH                  0x01
+#define SUBLANG_FRISIAN_NETHERLANDS     0x01
+#define SUBLANG_GALICIAN_GALICIAN       0x01
+#define SUBLANG_GEORGIAN_GEORGIA        0x01
+#define SUBLANG_GERMAN                  0x01
+#define SUBLANG_GREEK_GREECE            0x01
+#define SUBLANG_GREENLANDIC_GREENLAND   0x01
+#define SUBLANG_GUJARATI_INDIA          0x01
+#define SUBLANG_HAUSA_NIGERIA_LATIN     0x01
+#define SUBLANG_HAWAIIAN_US             0x01
+#define SUBLANG_HEBREW_ISRAEL           0x01
+#define SUBLANG_HINDI_INDIA             0x01
+#define SUBLANG_HUNGARIAN_HUNGARY       0x01
+#define SUBLANG_ICELANDIC_ICELAND       0x01
+#define SUBLANG_IGBO_NIGERIA            0x01
+#define SUBLANG_INDONESIAN_INDONESIA    0x01
+#define SUBLANG_INUKTITUT_CANADA        0x01
+#define SUBLANG_ITALIAN                 0x01
+#define SUBLANG_JAPANESE_JAPAN          0x01
+#define SUBLANG_KANNADA_INDIA           0x01
+#define SUBLANG_KAZAK_KAZAKHSTAN        0x01
+#define SUBLANG_KHMER_CAMBODIA          0x01
+#define SUBLANG_KICHE_GUATEMALA         0x01
+#define SUBLANG_KINYARWANDA_RWANDA      0x01
+#define SUBLANG_KONKANI_INDIA           0x01
+#define SUBLANG_KOREAN                  0x01
+#define SUBLANG_KYRGYZ_KYRGYZSTAN       0x01
+#define SUBLANG_LAO_LAO                 0x01
+#define SUBLANG_LATVIAN_LATVIA          0x01
+#define SUBLANG_LITHUANIAN              0x01
+#define SUBLANG_LUXEMBOURGISH_LUXEMBOURG 0x01
+#define SUBLANG_MACEDONIAN_MACEDONIA    0x01
+#define SUBLANG_MALAY_MALAYSIA          0x01
+#define SUBLANG_MALAYALAM_INDIA         0x01
+#define SUBLANG_MALTESE_MALTA           0x01
+#define SUBLANG_MAORI_NEW_ZEALAND       0x01
+#define SUBLANG_MAPUDUNGUN_CHILE        0x01
+#define SUBLANG_MARATHI_INDIA           0x01
+#define SUBLANG_MOHAWK_MOHAWK           0x01
+#define SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA 0x01
+#define SUBLANG_NEPALI_NEPAL            0x01
+#define SUBLANG_NORWEGIAN_BOKMAL        0x01
+#define SUBLANG_OCCITAN_FRANCE          0x01
+#define SUBLANG_ODIA_INDIA              0x01
+#define SUBLANG_ORIYA_INDIA             0x01
+#define SUBLANG_PASHTO_AFGHANISTAN      0x01
+#define SUBLANG_PERSIAN_IRAN            0x01
+#define SUBLANG_POLISH_POLAND           0x01
+#define SUBLANG_PORTUGUESE_BRAZILIAN    0x01
+#define SUBLANG_PUNJABI_INDIA           0x01
+#define SUBLANG_QUECHUA_BOLIVIA         0x01
+#define SUBLANG_ROMANIAN_ROMANIA        0x01
+#define SUBLANG_ROMANSH_SWITZERLAND     0x01
+#define SUBLANG_RUSSIAN_RUSSIA          0x01
+#define SUBLANG_SAKHA_RUSSIA            0x01
+#define SUBLANG_SAMI_NORTHERN_NORWAY    0x01
+#define SUBLANG_SANSKRIT_INDIA          0x01
+#define SUBLANG_SCOTTISH_GAELIC         0x01
+#define SUBLANG_SERBIAN_CROATIA         0x01
+#define SUBLANG_SINDHI_INDIA            0x01
+#define SUBLANG_SINHALESE_SRI_LANKA     0x01
+#define SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA 0x01
+#define SUBLANG_SLOVAK_SLOVAKIA         0x01
+#define SUBLANG_SLOVENIAN_SLOVENIA      0x01
+#define SUBLANG_SPANISH                 0x01
+#define SUBLANG_SWAHILI_KENYA           0x01
+#define SUBLANG_SWEDISH                 0x01
+#define SUBLANG_SYRIAC_SYRIA            0x01
+#define SUBLANG_TAJIK_TAJIKISTAN        0x01
+#define SUBLANG_TAMIL_INDIA             0x01
+#define SUBLANG_TATAR_RUSSIA            0x01
+#define SUBLANG_TELUGU_INDIA            0x01
+#define SUBLANG_THAI_THAILAND           0x01
+#define SUBLANG_TIBETAN_PRC             0x01
+#define SUBLANG_TIGRINYA_ETHIOPIA       0x01
+#define SUBLANG_TSWANA_SOUTH_AFRICA     0x01
+#define SUBLANG_TURKISH_TURKEY          0x01
+#define SUBLANG_TURKMEN_TURKMENISTAN    0x01
+#define SUBLANG_UIGHUR_PRC              0x01
+#define SUBLANG_UKRAINIAN_UKRAINE       0x01
+#define SUBLANG_UPPER_SORBIAN_GERMANY   0x01
+#define SUBLANG_URDU_PAKISTAN           0x01
+#define SUBLANG_UZBEK_LATIN             0x01
+#define SUBLANG_VIETNAMESE_VIETNAM      0x01
+#define SUBLANG_WELSH_UNITED_KINGDOM    0x01
+#define SUBLANG_WOLOF_SENEGAL           0x01
+#define SUBLANG_XHOSA_SOUTH_AFRICA      0x01
+#define SUBLANG_YAKUT_RUSSIA            0x01
+#define SUBLANG_YI_PRC                  0x01
+#define SUBLANG_YORUBA_NIGERIA          0x01
+#define SUBLANG_ZULU_SOUTH_AFRICA       0x01
+#define SORT_INVARIANT_MATH             0x1
+#define SORT_JAPANESE_UNICODE           0x1
+#define SORT_CHINESE_UNICODE            0x1
+#define SORT_KOREAN_UNICODE             0x1
+#define SORT_GERMAN_PHONE_BOOK          0x1
+#define SORT_HUNGARIAN_TECHNICAL        0x1
+#define SORT_GEORGIAN_MODERN            0x1
+#define __drv_typeCond                  1
+#define VS_VERSION_INFO                 1
+#define VFFF_ISSHAREDFILE               0x0001
+#define VFF_CURNEDEST                   0x0001
+#define VIFF_FORCEINSTALL               0x0001
+#define WINAPI_FAMILY_PC_APP            2
+#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2
+#define SW_SHOWMINIMIZED                2
+#define SHOW_ICONWINDOW                 2
+#define SW_OTHERZOOM                    2
+#define VK_RBUTTON                      0x02
+#define WM_DESTROY                      0x0002
+#define WA_CLICKACTIVE                  2
+#define PWR_SUSPENDRESUME               2
+#define NFR_UNICODE                     2
+#define UIS_CLEAR                       2
+#define UISF_HIDEACCEL                  0x2
+#define XBUTTON2                        0x0002
+#define WMSZ_RIGHT                      2
+#define HTCAPTION                       2
+#define SMTO_ABORTIFHUNG                0x0002
+#define MA_ACTIVATEANDEAT               2
+#define ICON_SMALL2                     2
+#define SIZE_MAXIMIZED                  2
+#define MK_RBUTTON                      0x0002
+#define TME_LEAVE                       0x00000002
+#define CS_HREDRAW                      0x0002
+#define CF_BITMAP                       2
+#define IDCANCEL                        2
+#define BN_HILITE                       2
+#define BST_INDETERMINATE               0x0002
+#define HDS_BUTTONS                     0x0002
+#define TBSTYLE_CHECK                   0x0002
+#define TTS_NOPREFIX                    0x02
+#define TBS_VERT                        0x0002
+#define UDS_SETBUDDYINT                 0x0002
+#define LWS_IGNORERETURN                0x0002
+#define LVS_SMALLICON                   0x0002
+#define TVS_HASLINES                    0x0002
+#define TVS_EX_MULTISELECT              0x0002
+#define TCS_BOTTOM                      0x0002
+#define TCS_RIGHT                       0x0002
+#define ACS_TRANSPARENT                 0x0002
+#define MCS_MULTISELECT                 0x0002
+#define DTS_SHOWNONE                    0x0002
+#define PGS_AUTOSCROLL                  0x00000002
+#define NFS_STATIC                      0x0002
+#define BCSIF_IMAGE                     0x0002
+#define BCSS_STRETCH                    0x0002
+#define LANG_BULGARIAN                  0x02
+#define SUBLANG_SYS_DEFAULT             0x02
+#define SUBLANG_ARABIC_IRAQ             0x02
+#define SUBLANG_AZERI_CYRILLIC          0x02
+#define SUBLANG_AZERBAIJANI_AZERBAIJAN_CYRILLIC 0x02
+#define SUBLANG_BANGLA_BANGLADESH       0x02
+#define SUBLANG_BENGALI_BANGLADESH      0x02
+#define SUBLANG_CHINESE_SIMPLIFIED      0x02
+#define SUBLANG_DUTCH_BELGIAN           0x02
+#define SUBLANG_ENGLISH_UK              0x02
+#define SUBLANG_FRENCH_BELGIAN          0x02
+#define SUBLANG_FULAH_SENEGAL           0x02
+#define SUBLANG_GERMAN_SWISS            0x02
+#define SUBLANG_INUKTITUT_CANADA_LATIN  0x02
+#define SUBLANG_IRISH_IRELAND           0x02
+#define SUBLANG_ITALIAN_SWISS           0x02
+#define SUBLANG_KASHMIRI_SASIA          0x02
+#define SUBLANG_KASHMIRI_INDIA          0x02
+#define SUBLANG_LOWER_SORBIAN_GERMANY   0x02
+#define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02
+#define SUBLANG_MONGOLIAN_PRC           0x02
+#define SUBLANG_NEPALI_INDIA            0x02
+#define SUBLANG_NORWEGIAN_NYNORSK       0x02
+#define SUBLANG_PORTUGUESE              0x02
+#define SUBLANG_PULAR_SENEGAL           0x02
+#define SUBLANG_PUNJABI_PAKISTAN        0x02
+#define SUBLANG_QUECHUA_ECUADOR         0x02
+#define SUBLANG_SAMI_NORTHERN_SWEDEN    0x02
+#define SUBLANG_SERBIAN_LATIN           0x02
+#define SUBLANG_SINDHI_PAKISTAN         0x02
+#define SUBLANG_SINDHI_AFGHANISTAN      0x02
+#define SUBLANG_SPANISH_MEXICAN         0x02
+#define SUBLANG_SWEDISH_FINLAND         0x02
+#define SUBLANG_TAMAZIGHT_ALGERIA_LATIN 0x02
+#define SUBLANG_TAMIL_SRI_LANKA         0x02
+#define SUBLANG_TIGRIGNA_ERITREA        0x02
+#define SUBLANG_TIGRINYA_ERITREA        0x02
+#define SUBLANG_TSWANA_BOTSWANA         0x02
+#define SUBLANG_URDU_INDIA              0x02
+#define SUBLANG_UZBEK_CYRILLIC          0x02
+#define SUBLANG_VALENCIAN_VALENCIA      0x02
+#define SORT_CHINESE_PRC                0x2
+#define __drv_typeBitset                2
+#define VFF_FILEINUSE                   0x0002
+#define VIFF_DONTDELETEOLD              0x0002
+#define WINAPI_FAMILY_PHONE_APP         3
+#define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID 3
+#define SW_SHOWMAXIMIZED                3
+#define SW_MAXIMIZE                     3
+#define SHOW_FULLSCREEN                 3
+#define SW_PARENTOPENING                3
+#define VK_CANCEL                       0x03
+#define WM_MOVE                         0x0003
+#define PWR_CRITICALRESUME              3
+#define NF_QUERY                        3
+#define UIS_INITIALIZE                  3
+#define WMSZ_TOP                        3
+#define HTSYSMENU                       3
+#define MA_NOACTIVATE                   3
+#define SIZE_MAXSHOW                    3
+#define CF_METAFILEPICT                 3
+#define IDABORT                         3
+#define BN_UNHILITE                     3
+#define LVS_LIST                        0x0003
+#define LVS_TYPEMASK                    0x0003
+#define LANG_CATALAN                    0x03
+#define LANG_VALENCIAN                  0x03
+#define SUBLANG_CUSTOM_DEFAULT          0x03
+#define SUBLANG_ARABIC_EGYPT            0x03
+#define SUBLANG_CHINESE_HONGKONG        0x03
+#define SUBLANG_ENGLISH_AUS             0x03
+#define SUBLANG_FRENCH_CANADIAN         0x03
+#define SUBLANG_GERMAN_AUSTRIAN         0x03
+#define SUBLANG_QUECHUA_PERU            0x03
+#define SUBLANG_SAMI_NORTHERN_FINLAND   0x03
+#define SUBLANG_SERBIAN_CYRILLIC        0x03
+#define SUBLANG_SPANISH_MODERN          0x03
+#define SORT_CHINESE_BOPOMOFO           0x3
+#define __drv_typeExpr                  3
+#define SW_SHOWNOACTIVATE               4
+#define SHOW_OPENNOACTIVATE             4
+#define SW_OTHERUNZOOM                  4
+#define VK_MBUTTON                      0x04
+#define NF_REQUERY                      4
+#define UISF_ACTIVE                     0x4
+#define WMSZ_TOPLEFT                    4
+#define HTGROWBOX                       4
+#define MA_NOACTIVATEANDEAT             4
+#define SIZE_MAXHIDE                    4
+#define MK_SHIFT                        0x0004
+#define CF_SYLK                         4
+#define IDRETRY                         4
+#define BN_DISABLE                      4
+#define BST_PUSHED                      0x0004
+#define HDS_HOTTRACK                    0x0004
+#define TBSTYLE_GROUP                   0x0004
+#define TBS_TOP                         0x0004
+#define TBS_LEFT                        0x0004
+#define UDS_ALIGNRIGHT                  0x0004
+#define PBS_VERTICAL                    0x04
+#define LWS_NOPREFIX                    0x0004
+#define LVS_SINGLESEL                   0x0004
+#define TVS_LINESATROOT                 0x0004
+#define TVS_EX_DOUBLEBUFFER             0x0004
+#define TCS_MULTISELECT                 0x0004
+#define ACS_AUTOPLAY                    0x0004
+#define MCS_WEEKNUMBERS                 0x0004
+#define DTS_LONGDATEFORMAT              0x0004
+#define PGS_DRAGNDROP                   0x00000004
+#define NFS_LISTCOMBO                   0x0004
+#define BCSIF_STYLE                     0x0004
+#define BCSS_ALIGNLEFT                  0x0004
+#define LANG_CHINESE                    0x04
+#define LANG_CHINESE_SIMPLIFIED         0x04
+#define SUBLANG_CUSTOM_UNSPECIFIED      0x04
+#define SUBLANG_ARABIC_LIBYA            0x04
+#define SUBLANG_CHINESE_SINGAPORE       0x04
+#define SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN 0x04
+#define SUBLANG_ENGLISH_CAN             0x04
+#define SUBLANG_FRENCH_SWISS            0x04
+#define SUBLANG_GERMAN_LUXEMBOURG       0x04
+#define SUBLANG_SAMI_LULE_NORWAY        0x04
+#define SUBLANG_SPANISH_GUATEMALA       0x04
+#define SUBLANG_TAMAZIGHT_MOROCCO_TIFINAGH 0x04
+#define SORT_JAPANESE_RADICALSTROKE     0x4
+#define SORT_CHINESE_RADICALSTROKE      0x4
+#define VFF_BUFFTOOSMALL                0x0004
+#define SW_SHOW                         5
+#define VK_XBUTTON1                     0x05
+#define WM_SIZE                         0x0005
+#define WMSZ_TOPRIGHT                   5
+#define HTMENU                          5
+#define CF_DIF                          5
+#define IDIGNORE                        5
+#define BN_DOUBLECLICKED                5
+#define LANG_CZECH                      0x05
+#define SUBLANG_UI_CUSTOM_DEFAULT       0x05
+#define SUBLANG_ARABIC_ALGERIA          0x05
+#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN 0x05
+#define SUBLANG_CHINESE_MACAU           0x05
+#define SUBLANG_ENGLISH_NZ              0x05
+#define SUBLANG_FRENCH_LUXEMBOURG       0x05
+#define SUBLANG_GERMAN_LIECHTENSTEIN    0x05
+#define SUBLANG_SAMI_LULE_SWEDEN        0x05
+#define SUBLANG_SPANISH_COSTA_RICA      0x05
+#define SW_MINIMIZE                     6
+#define VK_XBUTTON2                     0x06
+#define WM_ACTIVATE                     0x0006
+#define WMSZ_BOTTOM                     6
+#define HTHSCROLL                       6
+#define CF_TIFF                         6
+#define IDYES                           6
+#define BN_SETFOCUS                     6
+#define LANG_DANISH                     0x06
+#define SUBLANG_ARABIC_MOROCCO          0x06
+#define SUBLANG_ENGLISH_EIRE            0x06
+#define SUBLANG_FRENCH_MONACO           0x06
+#define SUBLANG_SAMI_SOUTHERN_NORWAY    0x06
+#define SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN 0x06
+#define SUBLANG_SPANISH_PANAMA          0x06
+#define SW_SHOWMINNOACTIVE              7
+#define WM_SETFOCUS                     0x0007
+#define WMSZ_BOTTOMLEFT                 7
+#define HTVSCROLL                       7
+#define CF_OEMTEXT                      7
+#define IDNO                            7
+#define BN_KILLFOCUS                    7
+#define LANG_GERMAN                     0x07
+#define SUBLANG_ARABIC_TUNISIA          0x07
+#define SUBLANG_ENGLISH_SOUTH_AFRICA    0x07
+#define SUBLANG_SAMI_SOUTHERN_SWEDEN    0x07
+#define SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x07
+#define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07
+#define SW_SHOWNA                       8
+#define VK_BACK                         0x08
+#define WM_KILLFOCUS                    0x0008
+#define WMSZ_BOTTOMRIGHT                8
+#define HTMINBUTTON                     8
+#define SMTO_NOTIMEOUTIFNOTHUNG         0x0008
+#define MK_CONTROL                      0x0008
+#define CS_DBLCLKS                      0x0008
+#define CF_DIB                          8
+#define IDCLOSE                         8
+#define BST_FOCUS                       0x0008
+#define HDS_HIDDEN                      0x0008
+#define TBSTYLE_DROPDOWN                0x0008
+#define TBS_BOTH                        0x0008
+#define UDS_ALIGNLEFT                   0x0008
+#define PBS_MARQUEE                     0x08
+#define LWS_USEVISUALSTYLE              0x0008
+#define LVS_SHOWSELALWAYS               0x0008
+#define TVS_EDITLABELS                  0x0008
+#define TVS_EX_NOINDENTSTATE            0x0008
+#define TCS_FLATBUTTONS                 0x0008
+#define ACS_TIMER                       0x0008
+#define MCS_NOTODAYCIRCLE               0x0008
+#define NFS_BUTTON                      0x0008
+#define BCSIF_SIZE                      0x0008
+#define BCSS_IMAGE                      0x0008
+#define LANG_GREEK                      0x08
+#define SUBLANG_ARABIC_OMAN             0x08
+#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x08
+#define SUBLANG_ENGLISH_JAMAICA         0x08
+#define SUBLANG_SAMI_SKOLT_FINLAND      0x08
+#define SUBLANG_SPANISH_VENEZUELA       0x08
+#define SW_RESTORE                      9
+#define VK_TAB                          0x09
+#define HTMAXBUTTON                     9
+#define CF_PALETTE                      9
+#define IDHELP                          9
+#define DTS_TIMEFORMAT                  0x0009
+#define LANG_ENGLISH                    0x09
+#define SUBLANG_ARABIC_YEMEN            0x09
+#define SUBLANG_ENGLISH_CARIBBEAN       0x09
+#define SUBLANG_SAMI_INARI_FINLAND      0x09
+#define SUBLANG_SERBIAN_SERBIA_LATIN    0x09
+#define SUBLANG_SPANISH_COLOMBIA        0x09
+#define SW_SHOWDEFAULT                  10
+#define WM_ENABLE                       0x000A
+#define HTLEFT                          10
+#define CF_PENDATA                      10
+#define IDTRYAGAIN                      10
+#define HELP_CONTEXTMENU                0x000a
+#define LANG_SPANISH                    0x0a
+#define SUBLANG_ARABIC_SYRIA            0x0a
+#define SUBLANG_ENGLISH_BELIZE          0x0a
+#define SUBLANG_SERBIAN_SERBIA_CYRILLIC 0x0a
+#define SUBLANG_SPANISH_PERU            0x0a
+#define SW_FORCEMINIMIZE                11
+#define SW_MAX                          11
+#define WM_SETREDRAW                    0x000B
+#define HTRIGHT                         11
+#define CF_RIFF                         11
+#define IDCONTINUE                      11
+#define HELP_FINDER                     0x000b
+#define LANG_FINNISH                    0x0b
+#define SUBLANG_ARABIC_JORDAN           0x0b
+#define SUBLANG_ENGLISH_TRINIDAD        0x0b
+#define SUBLANG_SERBIAN_MONTENEGRO_LATIN 0x0b
+#define SUBLANG_SPANISH_ARGENTINA       0x0b
+#define VK_CLEAR                        0x0C
+#define WM_SETTEXT                      0x000C
+#define HTTOP                           12
+#define CF_WAVE                         12
+#define HELP_WM_HELP                    0x000c
+#define DTS_SHORTDATECENTURYFORMAT      0x000C
+#define LANG_FRENCH                     0x0c
+#define SUBLANG_ARABIC_LEBANON          0x0c
+#define SUBLANG_ENGLISH_ZIMBABWE        0x0c
+#define SUBLANG_SERBIAN_MONTENEGRO_CYRILLIC 0x0c
+#define SUBLANG_SPANISH_ECUADOR         0x0c
+#define VK_RETURN                       0x0D
+#define WM_GETTEXT                      0x000D
+#define HTTOPLEFT                       13
+#define CF_UNICODETEXT                  13
+#define HELP_SETPOPUP_POS               0x000d
+#define LANG_HEBREW                     0x0d
+#define SUBLANG_ARABIC_KUWAIT           0x0d
+#define SUBLANG_ENGLISH_PHILIPPINES     0x0d
+#define SUBLANG_SPANISH_CHILE           0x0d
+#define WM_GETTEXTLENGTH                0x000E
+#define HTTOPRIGHT                      14
+#define CF_ENHMETAFILE                  14
+#define LANG_HUNGARIAN                  0x0e
+#define SUBLANG_ARABIC_UAE              0x0e
+#define SUBLANG_SPANISH_URUGUAY         0x0e
+#define WM_PAINT                        0x000F
+#define HTBOTTOM                        15
+#define CF_HDROP                        15
+#define LANG_ICELANDIC                  0x0f
+#define SUBLANG_ARABIC_BAHRAIN          0x0f
+#define SUBLANG_SPANISH_PARAGUAY        0x0f
+#define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID 16
+#define VK_SHIFT                        0x10
+#define WM_CLOSE                        0x0010
+#define HTBOTTOMLEFT                    16
+#define WVR_ALIGNTOP                    0x0010
+#define MK_MBUTTON                      0x0010
+#define TME_NONCLIENT                   0x00000010
+#define CF_LOCALE                       16
+#define HELP_TCARD_DATA                 0x0010
+#define TBSTYLE_AUTOSIZE                0x0010
+#define TTS_NOANIMATE                   0x10
+#define TBS_NOTICKS                     0x0010
+#define UDS_AUTOBUDDY                   0x0010
+#define PBS_SMOOTHREVERSE               0x10
+#define LWS_USECUSTOMTEXT               0x0010
+#define LVS_SORTASCENDING               0x0010
+#define TVS_DISABLEDRAGDROP             0x0010
+#define TVS_EX_RICHTOOLTIP              0x0010
+#define TCS_FORCEICONLEFT               0x0010
+#define MCS_NOTODAY                     0x0010
+#define DTS_APPCANPARSE                 0x0010
+#define NFS_ALL                         0x0010
+#define LANG_ITALIAN                    0x10
+#define SUBLANG_ARABIC_QATAR            0x10
+#define SUBLANG_ENGLISH_INDIA           0x10
+#define SUBLANG_SPANISH_BOLIVIA         0x10
+#define VK_CONTROL                      0x11
+#define WM_QUERYENDSESSION              0x0011
+#define HTBOTTOMRIGHT                   17
+#define CF_DIBV5                        17
+#define HELP_TCARD_OTHER_CALLER         0x0011
+#define LANG_JAPANESE                   0x11
+#define SUBLANG_ENGLISH_MALAYSIA        0x11
+#define SUBLANG_SPANISH_EL_SALVADOR     0x11
+#define VK_MENU                         0x12
+#define WM_QUIT                         0x0012
+#define HTBORDER                        18
+#define CF_MAX                          18
+#define LANG_KOREAN                     0x12
+#define SUBLANG_ENGLISH_SINGAPORE       0x12
+#define SUBLANG_SPANISH_HONDURAS        0x12
+#define VK_PAUSE                        0x13
+#define WM_QUERYOPEN                    0x0013
+#define HTOBJECT                        19
+#define LANG_DUTCH                      0x13
+#define SUBLANG_SPANISH_NICARAGUA       0x13
+#define VK_CAPITAL                      0x14
+#define WM_ERASEBKGND                   0x0014
+#define HTCLOSE                         20
+#define LANG_NORWEGIAN                  0x14
+#define SUBLANG_SPANISH_PUERTO_RICO     0x14
+#define _SAL_VERSION                    20
+#define VK_KANA                         0x15
+#define VK_HANGEUL                      0x15
+#define VK_HANGUL                       0x15
+#define WM_SYSCOLORCHANGE               0x0015
+#define HTHELP                          21
+#define LANG_POLISH                     0x15
+#define SUBLANG_SPANISH_US              0x15
+#define WM_ENDSESSION                   0x0016
+#define LANG_PORTUGUESE                 0x16
+#define VK_JUNJA                        0x17
+#define LANG_ROMANSH                    0x17
+#define RT_MANIFEST                     24
+#define VK_FINAL                        0x18
+#define WM_SHOWWINDOW                   0x0018
+#define LANG_ROMANIAN                   0x18
+#define VK_HANJA                        0x19
+#define VK_KANJI                        0x19
+#define LANG_RUSSIAN                    0x19
+#define WM_WININICHANGE                 0x001A
+#define LANG_BOSNIAN                    0x1a
+#define LANG_CROATIAN                   0x1a
+#define LANG_SERBIAN                    0x1a
+#define VK_ESCAPE                       0x1B
+#define WM_DEVMODECHANGE                0x001B
+#define LANG_SLOVAK                     0x1b
+#define VK_CONVERT                      0x1C
+#define WM_ACTIVATEAPP                  0x001C
+#define LANG_ALBANIAN                   0x1c
+#define VK_NONCONVERT                   0x1D
+#define WM_FONTCHANGE                   0x001D
+#define LANG_SWEDISH                    0x1d
+#define VK_ACCEPT                       0x1E
+#define WM_TIMECHANGE                   0x001E
+#define LANG_THAI                       0x1e
+#define VK_MODECHANGE                   0x1F
+#define WM_CANCELMODE                   0x001F
+#define LANG_TURKISH                    0x1f
+#define VK_SPACE                        0x20
+#define WM_SETCURSOR                    0x0020
+#define SMTO_ERRORONEXIT                0x0020
+#define WVR_ALIGNLEFT                   0x0020
+#define MK_XBUTTON1                     0x0020
+#define CS_OWNDC                        0x0020
+#define TBSTYLE_NOPREFIX                0x0020
+#define TTS_NOFADE                      0x20
+#define TBS_ENABLESELRANGE              0x0020
+#define UDS_ARROWKEYS                   0x0020
+#define LWS_RIGHT                       0x0020
+#define LVS_SORTDESCENDING              0x0020
+#define TVS_SHOWSELALWAYS               0x0020
+#define TVS_EX_AUTOHSCROLL              0x0020
+#define TCS_FORCELABELLEFT              0x0020
+#define DTS_RIGHTALIGN                  0x0020
+#define NFS_USEFONTASSOC                0x0020
+#define LANG_URDU                       0x20
+#define VK_PRIOR                        0x21
+#define WM_MOUSEACTIVATE                0x0021
+#define LANG_INDONESIAN                 0x21
+#define VK_NEXT                         0x22
+#define WM_CHILDACTIVATE                0x0022
+#define LANG_UKRAINIAN                  0x22
+#define VK_END                          0x23
+#define WM_QUEUESYNC                    0x0023
+#define LANG_BELARUSIAN                 0x23
+#define VK_HOME                         0x24
+#define WM_GETMINMAXINFO                0x0024
+#define LANG_SLOVENIAN                  0x24
+#define VK_LEFT                         0x25
+#define LANG_ESTONIAN                   0x25
+#define VK_UP                           0x26
+#define WM_PAINTICON                    0x0026
+#define LANG_LATVIAN                    0x26
+#define VK_RIGHT                        0x27
+#define WM_ICONERASEBKGND               0x0027
+#define LANG_LITHUANIAN                 0x27
+#define VK_DOWN                         0x28
+#define WM_NEXTDLGCTL                   0x0028
+#define LANG_TAJIK                      0x28
+#define VK_SELECT                       0x29
+#define LANG_FARSI                      0x29
+#define LANG_PERSIAN                    0x29
+#define VK_PRINT                        0x2A
+#define WM_SPOOLERSTATUS                0x002A
+#define LANG_VIETNAMESE                 0x2a
+#define VK_EXECUTE                      0x2B
+#define WM_DRAWITEM                     0x002B
+#define LANG_ARMENIAN                   0x2b
+#define VK_SNAPSHOT                     0x2C
+#define WM_MEASUREITEM                  0x002C
+#define LANG_AZERI                      0x2c
+#define LANG_AZERBAIJANI                0x2c
+#define VK_INSERT                       0x2D
+#define WM_DELETEITEM                   0x002D
+#define LANG_BASQUE                     0x2d
+#define VK_DELETE                       0x2E
+#define WM_VKEYTOITEM                   0x002E
+#define LANG_LOWER_SORBIAN              0x2e
+#define LANG_UPPER_SORBIAN              0x2e
+#define VK_HELP                         0x2F
+#define WM_CHARTOITEM                   0x002F
+#define LANG_MACEDONIAN                 0x2f
+#define WM_SETFONT                      0x0030
+#define WM_GETFONT                      0x0031
+#define WM_SETHOTKEY                    0x0032
+#define LANG_TSWANA                     0x32
+#define WM_GETHOTKEY                    0x0033
+#define LANG_XHOSA                      0x34
+#define LANG_ZULU                       0x35
+#define LANG_AFRIKAANS                  0x36
+#define WM_QUERYDRAGICON                0x0037
+#define LANG_GEORGIAN                   0x37
+#define LANG_FAEROESE                   0x38
+#define WM_COMPAREITEM                  0x0039
+#define LANG_HINDI                      0x39
+#define LANG_MALTESE                    0x3a
+#define LANG_SAMI                       0x3b
+#define LANG_IRISH                      0x3c
+#define WM_GETOBJECT                    0x003D
+#define LANG_MALAY                      0x3e
+#define LANG_KAZAK                      0x3f
+#define WVR_ALIGNBOTTOM                 0x0040
+#define MK_XBUTTON2                     0x0040
+#define CS_CLASSDC                      0x0040
+#define HDS_DRAGDROP                    0x0040
+#define BTNS_SHOWTEXT                   0x0040
+#define TTS_BALLOON                     0x40
+#define TBS_FIXEDLENGTH                 0x0040
+#define UDS_HORZ                        0x0040
+#define LVS_SHAREIMAGELISTS             0x0040
+#define TVS_RTLREADING                  0x0040
+#define TVS_EX_FADEINOUTEXPANDOS        0x0040
+#define TCS_HOTTRACK                    0x0040
+#define MCS_NOTRAILINGDATES             0x0040
+#define LANG_KYRGYZ                     0x40
+#define WM_COMPACTING                   0x0041
+#define LANG_SWAHILI                    0x41
+#define LANG_TURKMEN                    0x42
+#define LANG_UZBEK                      0x43
+#define WM_COMMNOTIFY                   0x0044
+#define LANG_TATAR                      0x44
+#define LANG_BANGLA                     0x45
+#define LANG_BENGALI                    0x45
+#define WM_WINDOWPOSCHANGING            0x0046
+#define LANG_PUNJABI                    0x46
+#define WM_WINDOWPOSCHANGED             0x0047
+#define LANG_GUJARATI                   0x47
+#define WM_POWER                        0x0048
+#define LANG_ODIA                       0x48
+#define LANG_ORIYA                      0x48
+#define LANG_TAMIL                      0x49
+#define WM_COPYDATA                     0x004A
+#define LANG_TELUGU                     0x4a
+#define WM_CANCELJOURNAL                0x004B
+#define LANG_KANNADA                    0x4b
+#define LANG_MALAYALAM                  0x4c
+#define LANG_ASSAMESE                   0x4d
+#define WM_NOTIFY                       0x004E
+#define LANG_MARATHI                    0x4e
+#define LANG_SANSKRIT                   0x4f
+#define WM_INPUTLANGCHANGEREQUEST       0x0050
+#define LANG_MONGOLIAN                  0x50
+#define WM_INPUTLANGCHANGE              0x0051
+#define LANG_TIBETAN                    0x51
+#define WM_TCARD                        0x0052
+#define LANG_WELSH                      0x52
+#define WM_HELP                         0x0053
+#define LANG_KHMER                      0x53
+#define WM_USERCHANGED                  0x0054
+#define LANG_LAO                        0x54
+#define WM_NOTIFYFORMAT                 0x0055
+#define LANG_GALICIAN                   0x56
+#define LANG_KONKANI                    0x57
+#define LANG_MANIPURI                   0x58
+#define LANG_SINDHI                     0x59
+#define LANG_SYRIAC                     0x5a
+#define VK_LWIN                         0x5B
+#define LANG_SINHALESE                  0x5b
+#define VK_RWIN                         0x5C
+#define LANG_CHEROKEE                   0x5c
+#define VK_APPS                         0x5D
+#define LANG_INUKTITUT                  0x5d
+#define LANG_AMHARIC                    0x5e
+#define VK_SLEEP                        0x5F
+#define LANG_TAMAZIGHT                  0x5f
+#define VK_NUMPAD0                      0x60
+#define LANG_KASHMIRI                   0x60
+#define VK_NUMPAD1                      0x61
+#define LANG_NEPALI                     0x61
+#define VK_NUMPAD2                      0x62
+#define LANG_FRISIAN                    0x62
+#define VK_NUMPAD3                      0x63
+#define LANG_PASHTO                     0x63
+#define WINAPI_FAMILY_DESKTOP_APP       100
+#define VK_NUMPAD4                      0x64
+#define LANG_FILIPINO                   0x64
+#define VS_USER_DEFINED                 100
+#define VK_NUMPAD5                      0x65
+#define LANG_DIVEHI                     0x65
+#define VK_NUMPAD6                      0x66
+#define VK_NUMPAD7                      0x67
+#define LANG_FULAH                      0x67
+#define LANG_PULAR                      0x67
+#define VK_NUMPAD8                      0x68
+#define LANG_HAUSA                      0x68
+#define VK_NUMPAD9                      0x69
+#define VK_MULTIPLY                     0x6A
+#define LANG_YORUBA                     0x6a
+#define VK_ADD                          0x6B
+#define LANG_QUECHUA                    0x6b
+#define VK_SEPARATOR                    0x6C
+#define LANG_SOTHO                      0x6c
+#define VK_SUBTRACT                     0x6D
+#define LANG_BASHKIR                    0x6d
+#define VK_DECIMAL                      0x6E
+#define LANG_LUXEMBOURGISH              0x6e
+#define VK_DIVIDE                       0x6F
+#define LANG_GREENLANDIC                0x6f
+#define VK_F1                           0x70
+#define LANG_IGBO                       0x70
+#define VK_F2                           0x71
+#define VK_F3                           0x72
+#define VK_F4                           0x73
+#define LANG_TIGRIGNA                   0x73
+#define LANG_TIGRINYA                   0x73
+#define VK_F5                           0x74
+#define VK_F6                           0x75
+#define LANG_HAWAIIAN                   0x75
+#define VK_F7                           0x76
+#define VK_F8                           0x77
+#define VK_F9                           0x78
+#define WHEEL_DELTA                     120
+#define LANG_YI                         0x78
+#define VK_F10                          0x79
+#define VK_F11                          0x7A
+#define LANG_MAPUDUNGUN                 0x7a
+#define VK_F12                          0x7B
+#define WM_CONTEXTMENU                  0x007B
+#define VK_F13                          0x7C
+#define WM_STYLECHANGING                0x007C
+#define LANG_MOHAWK                     0x7c
+#define VK_F14                          0x7D
+#define WM_STYLECHANGED                 0x007D
+#define VK_F15                          0x7E
+#define WM_DISPLAYCHANGE                0x007E
+#define LANG_BRETON                     0x7e
+#define VK_F16                          0x7F
+#define WM_GETICON                      0x007F
+#define LANG_INVARIANT                  0x7f
+#define VK_F17                          0x80
+#define WM_SETICON                      0x0080
+#define WVR_ALIGNRIGHT                  0x0080
+#define CS_PARENTDC                     0x0080
+#define CF_OWNERDISPLAY                 0x0080
+#define HDS_FULLDRAG                    0x0080
+#define BTNS_WHOLEDROPDOWN              0x0080
+#define TTS_CLOSE                       0x80
+#define TBS_NOTHUMB                     0x0080
+#define UDS_NOTHOUSANDS                 0x0080
+#define LVS_NOLABELWRAP                 0x0080
+#define TVS_NOTOOLTIPS                  0x0080
+#define TVS_EX_PARTIALCHECKBOXES        0x0080
+#define TCS_VERTICAL                    0x0080
+#define MCS_SHORTDAYSOFWEEK             0x0080
+#define LANG_UIGHUR                     0x80
+#define VK_F18                          0x81
+#define WM_NCCREATE                     0x0081
+#define CF_DSPTEXT                      0x0081
+#define LANG_MAORI                      0x81
+#define VK_F19                          0x82
+#define WM_NCDESTROY                    0x0082
+#define CF_DSPBITMAP                    0x0082
+#define LANG_OCCITAN                    0x82
+#define VK_F20                          0x83
+#define WM_NCCALCSIZE                   0x0083
+#define CF_DSPMETAFILEPICT              0x0083
+#define LANG_CORSICAN                   0x83
+#define VK_F21                          0x84
+#define WM_NCHITTEST                    0x0084
+#define LANG_ALSATIAN                   0x84
+#define VK_F22                          0x85
+#define WM_NCPAINT                      0x0085
+#define LANG_SAKHA                      0x85
+#define LANG_YAKUT                      0x85
+#define VK_F23                          0x86
+#define WM_NCACTIVATE                   0x0086
+#define LANG_KICHE                      0x86
+#define VK_F24                          0x87
+#define WM_GETDLGCODE                   0x0087
+#define LANG_KINYARWANDA                0x87
+#define WM_SYNCPAINT                    0x0088
+#define LANG_WOLOF                      0x88
+#define LANG_DARI                       0x8c
+#define CF_DSPENHMETAFILE               0x008E
+#define VK_NUMLOCK                      0x90
+#define VK_SCROLL                       0x91
+#define LANG_SCOTTISH_GAELIC            0x91
+#define VK_OEM_NEC_EQUAL                0x92
+#define VK_OEM_FJ_JISHO                 0x92
+#define LANG_CENTRAL_KURDISH            0x92
+#define VK_OEM_FJ_MASSHOU               0x93
+#define VK_OEM_FJ_TOUROKU               0x94
+#define VK_OEM_FJ_LOYA                  0x95
+#define VK_OEM_FJ_ROYA                  0x96
+#define VK_LSHIFT                       0xA0
+#define WM_NCMOUSEMOVE                  0x00A0
+#define VK_RSHIFT                       0xA1
+#define WM_NCLBUTTONDOWN                0x00A1
+#define VK_LCONTROL                     0xA2
+#define WM_NCLBUTTONUP                  0x00A2
+#define VK_RCONTROL                     0xA3
+#define WM_NCLBUTTONDBLCLK              0x00A3
+#define VK_LMENU                        0xA4
+#define WM_NCRBUTTONDOWN                0x00A4
+#define VK_RMENU                        0xA5
+#define WM_NCRBUTTONUP                  0x00A5
+#define VK_BROWSER_BACK                 0xA6
+#define WM_NCRBUTTONDBLCLK              0x00A6
+#define VK_BROWSER_FORWARD              0xA7
+#define WM_NCMBUTTONDOWN                0x00A7
+#define VK_BROWSER_REFRESH              0xA8
+#define WM_NCMBUTTONUP                  0x00A8
+#define VK_BROWSER_STOP                 0xA9
+#define WM_NCMBUTTONDBLCLK              0x00A9
+#define VK_BROWSER_SEARCH               0xAA
+#define VK_BROWSER_FAVORITES            0xAB
+#define WM_NCXBUTTONDOWN                0x00AB
+#define VK_BROWSER_HOME                 0xAC
+#define WM_NCXBUTTONUP                  0x00AC
+#define VK_VOLUME_MUTE                  0xAD
+#define WM_NCXBUTTONDBLCLK              0x00AD
+#define VK_VOLUME_DOWN                  0xAE
+#define VK_VOLUME_UP                    0xAF
+#define VK_MEDIA_NEXT_TRACK             0xB0
+#define EM_GETSEL                       0x00B0
+#define VK_MEDIA_PREV_TRACK             0xB1
+#define EM_SETSEL                       0x00B1
+#define VK_MEDIA_STOP                   0xB2
+#define EM_GETRECT                      0x00B2
+#define VK_MEDIA_PLAY_PAUSE             0xB3
+#define EM_SETRECT                      0x00B3
+#define VK_LAUNCH_MAIL                  0xB4
+#define EM_SETRECTNP                    0x00B4
+#define VK_LAUNCH_MEDIA_SELECT          0xB5
+#define EM_SCROLL                       0x00B5
+#define VK_LAUNCH_APP1                  0xB6
+#define EM_LINESCROLL                   0x00B6
+#define VK_LAUNCH_APP2                  0xB7
+#define EM_SCROLLCARET                  0x00B7
+#define EM_GETMODIFY                    0x00B8
+#define EM_SETMODIFY                    0x00B9
+#define VK_OEM_1                        0xBA
+#define EM_GETLINECOUNT                 0x00BA
+#define VK_OEM_PLUS                     0xBB
+#define EM_LINEINDEX                    0x00BB
+#define VK_OEM_COMMA                    0xBC
+#define EM_SETHANDLE                    0x00BC
+#define VK_OEM_MINUS                    0xBD
+#define EM_GETHANDLE                    0x00BD
+#define VK_OEM_PERIOD                   0xBE
+#define EM_GETTHUMB                     0x00BE
+#define VK_OEM_2                        0xBF
+#define VK_OEM_3                        0xC0
+#define EM_LINELENGTH                   0x00C1
+#define EM_REPLACESEL                   0x00C2
+#define EM_GETLINE                      0x00C4
+#define EM_LIMITTEXT                    0x00C5
+#define EM_CANUNDO                      0x00C6
+#define EM_UNDO                         0x00C7
+#define EM_FMTLINES                     0x00C8
+#define EM_LINEFROMCHAR                 0x00C9
+#define EM_SETTABSTOPS                  0x00CB
+#define EM_SETPASSWORDCHAR              0x00CC
+#define EM_EMPTYUNDOBUFFER              0x00CD
+#define EM_GETFIRSTVISIBLELINE          0x00CE
+#define EM_SETREADONLY                  0x00CF
+#define EM_SETWORDBREAKPROC             0x00D0
+#define EM_GETWORDBREAKPROC             0x00D1
+#define EM_GETPASSWORDCHAR              0x00D2
+#define EM_SETMARGINS                   0x00D3
+#define EM_GETMARGINS                   0x00D4
+#define EM_GETLIMITTEXT                 0x00D5
+#define EM_POSFROMCHAR                  0x00D6
+#define EM_CHARFROMPOS                  0x00D7
+#define EM_SETIMESTATUS                 0x00D8
+#define EM_GETIMESTATUS                 0x00D9
+#define VK_OEM_4                        0xDB
+#define VK_OEM_5                        0xDC
+#define VK_OEM_6                        0xDD
+#define VK_OEM_7                        0xDE
+#define VK_OEM_8                        0xDF
+#define VK_OEM_AX                       0xE1
+#define VK_OEM_102                      0xE2
+#define VK_ICO_HELP                     0xE3
+#define VK_ICO_00                       0xE4
+#define VK_PROCESSKEY                   0xE5
+#define VK_ICO_CLEAR                    0xE6
+#define VK_PACKET                       0xE7
+#define VK_OEM_RESET                    0xE9
+#define VK_OEM_JUMP                     0xEA
+#define VK_OEM_PA1                      0xEB
+#define VK_OEM_PA2                      0xEC
+#define VK_OEM_PA3                      0xED
+#define VK_OEM_WSCTRL                   0xEE
+#define VK_OEM_CUSEL                    0xEF
+#define VK_OEM_ATTN                     0xF0
+#define BM_GETCHECK                     0x00F0
+#define VK_OEM_FINISH                   0xF1
+#define BM_SETCHECK                     0x00F1
+#define VK_OEM_COPY                     0xF2
+#define BM_GETSTATE                     0x00F2
+#define VK_OEM_AUTO                     0xF3
+#define BM_SETSTATE                     0x00F3
+#define VK_OEM_ENLW                     0xF4
+#define BM_SETSTYLE                     0x00F4
+#define VK_OEM_BACKTAB                  0xF5
+#define BM_CLICK                        0x00F5
+#define VK_ATTN                         0xF6
+#define BM_GETIMAGE                     0x00F6
+#define VK_CRSEL                        0xF7
+#define BM_SETIMAGE                     0x00F7
+#define VK_EXSEL                        0xF8
+#define BM_SETDONTCLICK                 0x00F8
+#define VK_EREOF                        0xF9
+#define VK_PLAY                         0xFA
+#define VK_ZOOM                         0xFB
+#define VK_NONAME                       0xFC
+#define VK_PA1                          0xFD
+#define VK_OEM_CLEAR                    0xFE
+#define WM_INPUT_DEVICE_CHANGE          0x00FE
+#define SUBVERSION_MASK                 0x000000FF
+#define WM_INPUT                        0x00FF
+#define WM_KEYFIRST                     0x0100
+#define WM_KEYDOWN                      0x0100
+#define WVR_HREDRAW                     0x0100
+#define HDS_FILTERBAR                   0x0100
+#define TBSTYLE_TOOLTIPS                0x0100
+#define RBS_TOOLTIPS                    0x00000100
+#define TTS_USEVISUALSTYLE              0x100
+#define SBARS_SIZEGRIP                  0x0100
+#define TBS_TOOLTIPS                    0x0100
+#define UDS_HOTTRACK                    0x0100
+#define LVS_AUTOARRANGE                 0x0100
+#define TVS_CHECKBOXES                  0x0100
+#define TVS_EX_EXCLUSIONCHECKBOXES      0x0100
+#define TCS_BUTTONS                     0x0100
+#define MCS_NOSELCHANGEONNAV            0x0100
+#define WM_KEYUP                        0x0101
+#define WM_CHAR                         0x0102
+#define WM_DEADCHAR                     0x0103
+#define WM_SYSKEYDOWN                   0x0104
+#define WM_SYSKEYUP                     0x0105
+#define WM_SYSCHAR                      0x0106
+#define WM_SYSDEADCHAR                  0x0107
+#define WM_UNICHAR                      0x0109
+#define WM_KEYLAST                      0x0109
+#define WM_IME_STARTCOMPOSITION         0x010D
+#define WM_IME_ENDCOMPOSITION           0x010E
+#define WM_IME_COMPOSITION              0x010F
+#define WM_IME_KEYLAST                  0x010F
+#define WM_INITDIALOG                   0x0110
+#define WM_COMMAND                      0x0111
+#define WM_SYSCOMMAND                   0x0112
+#define WM_TIMER                        0x0113
+#define WM_HSCROLL                      0x0114
+#define WM_VSCROLL                      0x0115
+#define WM_INITMENU                     0x0116
+#define WM_INITMENUPOPUP                0x0117
+#define WM_GESTURE                      0x0119
+#define WM_GESTURENOTIFY                0x011A
+#define WM_MENUSELECT                   0x011F
+#define WM_MENUCHAR                     0x0120
+#define WM_ENTERIDLE                    0x0121
+#define WM_MENURBUTTONUP                0x0122
+#define WM_MENUDRAG                     0x0123
+#define WM_MENUGETOBJECT                0x0124
+#define WM_UNINITMENUPOPUP              0x0125
+#define WM_MENUCOMMAND                  0x0126
+#define WM_CHANGEUISTATE                0x0127
+#define WM_UPDATEUISTATE                0x0128
+#define WM_QUERYUISTATE                 0x0129
+#define WM_CTLCOLORMSGBOX               0x0132
+#define WM_CTLCOLOREDIT                 0x0133
+#define WM_CTLCOLORLISTBOX              0x0134
+#define WM_CTLCOLORBTN                  0x0135
+#define WM_CTLCOLORDLG                  0x0136
+#define WM_CTLCOLORSCROLLBAR            0x0137
+#define WM_CTLCOLORSTATIC               0x0138
+#define MN_GETHMENU                     0x01E1
+#define _WIN32_IE_IE20                  0x0200
+#define WM_MOUSEFIRST                   0x0200
+#define WM_MOUSEMOVE                    0x0200
+#define WVR_VREDRAW                     0x0200
+#define CS_NOCLOSE                      0x0200
+#define CF_PRIVATEFIRST                 0x0200
+#define HDS_FLAT                        0x0200
+#define TBSTYLE_WRAPABLE                0x0200
+#define RBS_VARHEIGHT                   0x00000200
+#define TBS_REVERSED                    0x0200
+#define LVS_EDITLABELS                  0x0200
+#define TVS_TRACKSELECT                 0x0200
+#define TVS_EX_DIMMEDCHECKBOXES         0x0200
+#define TCS_MULTILINE                   0x0200
+#define WM_LBUTTONDOWN                  0x0201
+#define WM_LBUTTONUP                    0x0202
+#define WM_LBUTTONDBLCLK                0x0203
+#define WM_RBUTTONDOWN                  0x0204
+#define WM_RBUTTONUP                    0x0205
+#define WM_RBUTTONDBLCLK                0x0206
+#define WM_MBUTTONDOWN                  0x0207
+#define WM_MBUTTONUP                    0x0208
+#define WM_MBUTTONDBLCLK                0x0209
+#define WM_MOUSEWHEEL                   0x020A
+#define WM_XBUTTONDOWN                  0x020B
+#define WM_XBUTTONUP                    0x020C
+#define WM_XBUTTONDBLCLK                0x020D
+#define WM_MOUSEHWHEEL                  0x020E
+#define WM_MOUSELAST                    0x020E
+#define WM_PARENTNOTIFY                 0x0210
+#define WM_ENTERMENULOOP                0x0211
+#define WM_EXITMENULOOP                 0x0212
+#define WM_NEXTMENU                     0x0213
+#define WM_SIZING                       0x0214
+#define WM_CAPTURECHANGED               0x0215
+#define WM_MOVING                       0x0216
+#define WM_POWERBROADCAST               0x0218
+#define WM_DEVICECHANGE                 0x0219
+#define WM_MDICREATE                    0x0220
+#define WM_MDIDESTROY                   0x0221
+#define WM_MDIACTIVATE                  0x0222
+#define WM_MDIRESTORE                   0x0223
+#define WM_MDINEXT                      0x0224
+#define WM_MDIMAXIMIZE                  0x0225
+#define WM_MDITILE                      0x0226
+#define WM_MDICASCADE                   0x0227
+#define WM_MDIICONARRANGE               0x0228
+#define WM_MDIGETACTIVE                 0x0229
+#define WM_MDISETMENU                   0x0230
+#define WM_ENTERSIZEMOVE                0x0231
+#define WM_EXITSIZEMOVE                 0x0232
+#define WM_DROPFILES                    0x0233
+#define WM_MDIREFRESHMENU               0x0234
+#define WM_POINTERDEVICECHANGE          0x238
+#define WM_POINTERDEVICEINRANGE         0x239
+#define WM_POINTERDEVICEOUTOFRANGE      0x23A
+#define WM_TOUCH                        0x0240
+#define WM_NCPOINTERUPDATE              0x0241
+#define WM_NCPOINTERDOWN                0x0242
+#define WM_NCPOINTERUP                  0x0243
+#define WM_POINTERUPDATE                0x0245
+#define WM_POINTERDOWN                  0x0246
+#define WM_POINTERUP                    0x0247
+#define WM_POINTERENTER                 0x0249
+#define WM_POINTERLEAVE                 0x024A
+#define WM_POINTERACTIVATE              0x024B
+#define WM_POINTERCAPTURECHANGED        0x024C
+#define WM_TOUCHHITTESTING              0x024D
+#define WM_POINTERWHEEL                 0x024E
+#define WM_POINTERHWHEEL                0x024F
+#define DM_POINTERHITTEST               0x0250
+#define WM_IME_SETCONTEXT               0x0281
+#define WM_IME_NOTIFY                   0x0282
+#define WM_IME_CONTROL                  0x0283
+#define WM_IME_COMPOSITIONFULL          0x0284
+#define WM_IME_SELECT                   0x0285
+#define WM_IME_CHAR                     0x0286
+#define WM_IME_REQUEST                  0x0288
+#define WM_IME_KEYDOWN                  0x0290
+#define WM_IME_KEYUP                    0x0291
+#define WM_NCMOUSEHOVER                 0x02A0
+#define WM_MOUSEHOVER                   0x02A1
+#define WM_NCMOUSELEAVE                 0x02A2
+#define WM_MOUSELEAVE                   0x02A3
+#define WM_WTSSESSION_CHANGE            0x02B1
+#define WM_TABLET_FIRST                 0x02c0
+#define WM_TABLET_LAST                  0x02df
+#define WM_DPICHANGED                   0x02E0
+#define CF_PRIVATELAST                  0x02FF
+#define _WIN32_IE_IE30                  0x0300
+#define WM_CUT                          0x0300
+#define CF_GDIOBJFIRST                  0x0300
+#define WM_COPY                         0x0301
+#define _WIN32_IE_IE302                 0x0302
+#define WM_PASTE                        0x0302
+#define WM_CLEAR                        0x0303
+#define WM_UNDO                         0x0304
+#define WM_RENDERFORMAT                 0x0305
+#define WM_RENDERALLFORMATS             0x0306
+#define WM_DESTROYCLIPBOARD             0x0307
+#define WM_DRAWCLIPBOARD                0x0308
+#define WM_PAINTCLIPBOARD               0x0309
+#define WM_VSCROLLCLIPBOARD             0x030A
+#define WM_SIZECLIPBOARD                0x030B
+#define WM_ASKCBFORMATNAME              0x030C
+#define WM_CHANGECBCHAIN                0x030D
+#define WM_HSCROLLCLIPBOARD             0x030E
+#define WM_QUERYNEWPALETTE              0x030F
+#define WM_PALETTEISCHANGING            0x0310
+#define WM_PALETTECHANGED               0x0311
+#define WM_HOTKEY                       0x0312
+#define WM_PRINT                        0x0317
+#define WM_PRINTCLIENT                  0x0318
+#define WM_APPCOMMAND                   0x0319
+#define WM_THEMECHANGED                 0x031A
+#define WM_CLIPBOARDUPDATE              0x031D
+#define WM_DWMCOMPOSITIONCHANGED        0x031E
+#define WM_DWMNCRENDERINGCHANGED        0x031F
+#define WM_DWMCOLORIZATIONCOLORCHANGED  0x0320
+#define WM_DWMWINDOWMAXIMIZEDCHANGE     0x0321
+#define WM_DWMSENDICONICTHUMBNAIL       0x0323
+#define WM_DWMSENDICONICLIVEPREVIEWBITMAP 0x0326
+#define WM_GETTITLEBARINFOEX            0x033F
+#define WM_HANDHELDFIRST                0x0358
+#define WM_HANDHELDLAST                 0x035F
+#define WM_AFXFIRST                     0x0360
+#define WM_AFXLAST                      0x037F
+#define WM_PENWINFIRST                  0x0380
+#define WM_PENWINLAST                   0x038F
+#define WM_DDE_FIRST                    0x03E0
+#define CF_GDIOBJLAST                   0x03FF
+#define _WIN32_WINNT_NT4                0x0400
+#define _WIN32_IE_IE40                  0x0400
+#define WM_USER                         0x0400
+#define WVR_VALIDRECTS                  0x0400
+#define HDS_CHECKBOXES                  0x0400
+#define TBSTYLE_ALTDRAG                 0x0400
+#define RBS_BANDBORDERS                 0x00000400
+#define TBS_DOWNISLEFT                  0x0400
+#define LVS_OWNERDRAWFIXED              0x0400
+#define TVS_SINGLEEXPAND                0x0400
+#define TVS_EX_DRAWIMAGEASYNC           0x0400
+#define TCS_FIXEDWIDTH                  0x0400
+#define ctlFirst                        0x0400
+#define psh1                            0x0400
+#define _WIN32_IE_IE401                 0x0401
+#define psh2                            0x0401
+#define psh3                            0x0402
+#define psh4                            0x0403
+#define psh5                            0x0404
+#define psh6                            0x0405
+#define psh7                            0x0406
+#define psh8                            0x0407
+#define psh9                            0x0408
+#define psh10                           0x0409
+#define psh11                           0x040a
+#define psh12                           0x040b
+#define psh13                           0x040c
+#define psh14                           0x040d
+#define psh15                           0x040e
+#define psh16                           0x040f
+#define _WIN32_WINDOWS                  0x0410
+#define chx1                            0x0410
+#define chx2                            0x0411
+#define chx3                            0x0412
+#define chx4                            0x0413
+#define chx5                            0x0414
+#define chx6                            0x0415
+#define chx7                            0x0416
+#define chx8                            0x0417
+#define chx9                            0x0418
+#define chx10                           0x0419
+#define chx11                           0x041a
+#define chx12                           0x041b
+#define chx13                           0x041c
+#define chx14                           0x041d
+#define chx15                           0x041e
+#define chx16                           0x041f
+#define rad1                            0x0420
+#define rad2                            0x0421
+#define rad3                            0x0422
+#define rad4                            0x0423
+#define rad5                            0x0424
+#define rad6                            0x0425
+#define rad7                            0x0426
+#define rad8                            0x0427
+#define rad9                            0x0428
+#define rad10                           0x0429
+#define rad11                           0x042a
+#define rad12                           0x042b
+#define rad13                           0x042c
+#define rad14                           0x042d
+#define rad15                           0x042e
+#define rad16                           0x042f
+#define grp1                            0x0430
+#define grp2                            0x0431
+#define grp3                            0x0432
+#define grp4                            0x0433
+#define frm1                            0x0434
+#define frm2                            0x0435
+#define frm3                            0x0436
+#define frm4                            0x0437
+#define rct1                            0x0438
+#define rct2                            0x0439
+#define rct3                            0x043a
+#define rct4                            0x043b
+#define ico1                            0x043c
+#define ico2                            0x043d
+#define ico3                            0x043e
+#define ico4                            0x043f
+#define stc1                            0x0440
+#define stc2                            0x0441
+#define stc3                            0x0442
+#define stc4                            0x0443
+#define stc5                            0x0444
+#define stc6                            0x0445
+#define stc7                            0x0446
+#define stc8                            0x0447
+#define stc9                            0x0448
+#define stc10                           0x0449
+#define stc11                           0x044a
+#define stc12                           0x044b
+#define stc13                           0x044c
+#define stc14                           0x044d
+#define stc15                           0x044e
+#define stc16                           0x044f
+#define stc17                           0x0450
+#define stc18                           0x0451
+#define stc19                           0x0452
+#define stc20                           0x0453
+#define stc21                           0x0454
+#define stc22                           0x0455
+#define stc23                           0x0456
+#define stc24                           0x0457
+#define stc25                           0x0458
+#define stc26                           0x0459
+#define stc27                           0x045a
+#define stc28                           0x045b
+#define stc29                           0x045c
+#define stc30                           0x045d
+#define stc31                           0x045e
+#define stc32                           0x045f
+#define lst1                            0x0460
+#define lst2                            0x0461
+#define lst3                            0x0462
+#define lst4                            0x0463
+#define lst5                            0x0464
+#define lst6                            0x0465
+#define lst7                            0x0466
+#define lst8                            0x0467
+#define lst9                            0x0468
+#define lst10                           0x0469
+#define lst11                           0x046a
+#define lst12                           0x046b
+#define lst13                           0x046c
+#define lst14                           0x046d
+#define lst15                           0x046e
+#define lst16                           0x046f
+#define cmb1                            0x0470
+#define cmb2                            0x0471
+#define cmb3                            0x0472
+#define cmb4                            0x0473
+#define cmb5                            0x0474
+#define cmb6                            0x0475
+#define cmb7                            0x0476
+#define cmb8                            0x0477
+#define cmb9                            0x0478
+#define cmb10                           0x0479
+#define cmb11                           0x047a
+#define cmb12                           0x047b
+#define cmb13                           0x047c
+#define cmb14                           0x047d
+#define cmb15                           0x047e
+#define cmb16                           0x047f
+#define edt1                            0x0480
+#define edt2                            0x0481
+#define edt3                            0x0482
+#define edt4                            0x0483
+#define edt5                            0x0484
+#define edt6                            0x0485
+#define edt7                            0x0486
+#define edt8                            0x0487
+#define edt9                            0x0488
+#define edt10                           0x0489
+#define edt11                           0x048a
+#define edt12                           0x048b
+#define edt13                           0x048c
+#define edt14                           0x048d
+#define edt15                           0x048e
+#define edt16                           0x048f
+#define scr1                            0x0490
+#define scr2                            0x0491
+#define scr3                            0x0492
+#define scr4                            0x0493
+#define scr5                            0x0494
+#define scr6                            0x0495
+#define scr7                            0x0496
+#define scr8                            0x0497
+#define ctl1                            0x04A0
+#define ctlLast                         0x04ff
+#define _WIN32_WINNT_WIN2K              0x0500
+#define _WIN32_IE_IE50                  0x0500
+#define _WIN32_WINNT_WINXP              0x0501
+#define _WIN32_IE_IE501                 0x0501
+#define _WIN32_WINNT_WS03               0x0502
+#define _WIN32_IE_IE55                  0x0550
+#define _WIN32_WINNT_WIN6               0x0600
+#define _WIN32_WINNT_VISTA              0x0600
+#define _WIN32_WINNT_WS08               0x0600
+#define _WIN32_WINNT_LONGHORN           0x0600
+#define _WIN32_IE_IE60                  0x0600
+#define FILEOPENORD                     1536
+#define _WIN32_WINNT_WIN7               0x0601
+#define _WIN32_IE_IE60SP1               0x0601
+#define MULTIFILEOPENORD                1537
+#define _WIN32_WINNT_WIN8               0x0602
+#define _WIN32_IE_WS03                  0x0602
+#define PRINTDLGORD                     1538
+#define _WIN32_WINNT_WINBLUE            0x0603
+#define _WIN32_IE_IE60SP2               0x0603
+#define _WIN32_WINNT                    0x0603
+#define PRNSETUPDLGORD                  1539
+#define FINDDLGORD                      1540
+#define REPLACEDLGORD                   1541
+#define FONTDLGORD                      1542
+#define FORMATDLGORD31                  1543
+#define FORMATDLGORD30                  1544
+#define RUNDLGORD                       1545
+#define PAGESETUPDLGORD                 1546
+#define NEWFILEOPENORD                  1547
+#define PRINTDLGEXORD                   1549
+#define PAGESETUPDLGORDMOTIF            1550
+#define COLORMGMTDLGORD                 1551
+#define NEWFILEOPENV2ORD                1552
+#define NEWFILEOPENV3ORD                1553
+#define NEWFORMATDLGWITHLINK            1591
+#define IDC_MANAGE_LINK                 1592
+#define _WIN32_IE_IE70                  0x0700
+#define _WIN32_IE_IE80                  0x0800
+#define CS_SAVEBITS                     0x0800
+#define HDS_NOSIZING                    0x0800
+#define TBSTYLE_FLAT                    0x0800
+#define RBS_FIXEDORDER                  0x00000800
+#define SBARS_TOOLTIPS                  0x0800
+#define SBT_TOOLTIPS                    0x0800
+#define TBS_NOTIFYBEFOREMOVE            0x0800
+#define LVS_ALIGNLEFT                   0x0800
+#define TVS_INFOTIP                     0x0800
+#define TCS_RAGGEDRIGHT                 0x0800
+#define _WIN32_IE_IE90                  0x0900
+#define _WIN32_IE_IE100                 0x0A00
+#define _WIN32_IE                       0x0A00
+#define LVS_ALIGNMASK                   0x0c00
+#define CS_BYTEALIGNCLIENT              0x1000
+#define HDS_OVERFLOW                    0x1000
+#define TBSTYLE_LIST                    0x1000
+#define RBS_REGISTERDROP                0x00001000
+#define TBS_TRANSPARENTBKGND            0x1000
+#define LVS_OWNERDATA                   0x1000
+#define TVS_FULLROWSELECT               0x1000
+#define TCS_FOCUSONBUTTONDOWN           0x1000
+#define CS_BYTEALIGNWINDOW              0x2000
+#define TBSTYLE_CUSTOMERASE             0x2000
+#define RBS_AUTOSIZE                    0x00002000
+#define LVS_NOSCROLL                    0x2000
+#define TVS_NOSCROLL                    0x2000
+#define TCS_OWNERDRAWFIXED              0x2000
+#define CS_GLOBALCLASS                  0x4000
+#define TBSTYLE_REGISTERDROP            0x4000
+#define RBS_VERTICALGRIPPER             0x00004000
+#define LVS_NOCOLUMNHEADER              0x4000
+#define TVS_NONEVENHEIGHT               0x4000
+#define TCS_TOOLTIPS                    0x4000
+#define IDH_NO_HELP                     28440
+#define IDH_MISSING_CONTEXT             28441
+#define IDH_GENERIC_HELP_BUTTON         28442
+#define IDH_OK                          28443
+#define IDH_CANCEL                      28444
+#define IDH_HELP                        28445
+#define LANG_BOSNIAN_NEUTRAL            0x781a
+#define LANG_CHINESE_TRADITIONAL        0x7c04
+#define LANG_SERBIAN_NEUTRAL            0x7c1a
+#define IDTIMEOUT                       32000
+#define OCR_NORMAL                      32512
+#define OIC_SAMPLE                      32512
+#define IDI_APPLICATION                 32512
+#define OCR_IBEAM                       32513
+#define OIC_HAND                        32513
+#define IDI_HAND                        32513
+#define OCR_WAIT                        32514
+#define OIC_QUES                        32514
+#define IDI_QUESTION                    32514
+#define OCR_CROSS                       32515
+#define OIC_BANG                        32515
+#define IDI_EXCLAMATION                 32515
+#define OCR_UP                          32516
+#define OIC_NOTE                        32516
+#define IDI_ASTERISK                    32516
+#define OIC_WINLOGO                     32517
+#define IDI_WINLOGO                     32517
+#define OIC_SHIELD                      32518
+#define IDI_SHIELD                      32518
+#define OCR_SIZE                        32640
+#define OCR_ICON                        32641
+#define OCR_SIZENWSE                    32642
+#define OCR_SIZENESW                    32643
+#define OCR_SIZEWE                      32644
+#define OCR_SIZENS                      32645
+#define OCR_SIZEALL                     32646
+#define OCR_ICOCUR                      32647
+#define OCR_NO                          32648
+#define OCR_HAND                        32649
+#define OCR_APPSTARTING                 32650
+#define OBM_LFARROWI                    32734
+#define OBM_RGARROWI                    32735
+#define OBM_DNARROWI                    32736
+#define OBM_UPARROWI                    32737
+#define OBM_COMBO                       32738
+#define OBM_MNARROW                     32739
+#define OBM_LFARROWD                    32740
+#define OBM_RGARROWD                    32741
+#define OBM_DNARROWD                    32742
+#define OBM_UPARROWD                    32743
+#define OBM_RESTORED                    32744
+#define OBM_ZOOMD                       32745
+#define OBM_REDUCED                     32746
+#define OBM_RESTORE                     32747
+#define OBM_ZOOM                        32748
+#define OBM_REDUCE                      32749
+#define OBM_LFARROW                     32750
+#define OBM_RGARROW                     32751
+#define OBM_DNARROW                     32752
+#define OBM_UPARROW                     32753
+#define OBM_CLOSE                       32754
+#define OBM_OLD_RESTORE                 32755
+#define OBM_OLD_ZOOM                    32756
+#define OBM_OLD_REDUCE                  32757
+#define OBM_BTNCORNERS                  32758
+#define OBM_CHECKBOXES                  32759
+#define OBM_CHECK                       32760
+#define OBM_BTSIZE                      32761
+#define OBM_OLD_LFARROW                 32762
+#define OBM_OLD_RGARROW                 32763
+#define OBM_OLD_DNARROW                 32764
+#define OBM_OLD_UPARROW                 32765
+#define OBM_SIZE                        32766
+#define OBM_OLD_CLOSE                   32767
+#define WM_APP                          0x8000
+#define HELP_TCARD                      0x8000
+#define TBSTYLE_TRANSPARENT             0x8000
+#define RBS_DBLCLKTOGGLE                0x00008000
+#define LVS_NOSORTHEADER                0x8000
+#define TVS_NOHSCROLL                   0x8000
+#define TCS_FOCUSNEVER                  0x8000
+#define SC_SIZE                         0xF000
+#define SC_SEPARATOR                    0xF00F
+#define SC_MOVE                         0xF010
+#define SC_MINIMIZE                     0xF020
+#define SC_MAXIMIZE                     0xF030
+#define SC_NEXTWINDOW                   0xF040
+#define SC_PREVWINDOW                   0xF050
+#define SC_CLOSE                        0xF060
+#define SC_VSCROLL                      0xF070
+#define SC_HSCROLL                      0xF080
+#define SC_MOUSEMENU                    0xF090
+#define SC_KEYMENU                      0xF100
+#define SC_ARRANGE                      0xF110
+#define SC_RESTORE                      0xF120
+#define SC_TASKLIST                     0xF130
+#define SC_SCREENSAVE                   0xF140
+#define SC_HOTKEY                       0xF150
+#define SC_DEFAULT                      0xF160
+#define SC_MONITORPOWER                 0xF170
+#define SC_CONTEXTHELP                  0xF180
+#define LVS_TYPESTYLEMASK               0xfc00
+#define SPVERSION_MASK                  0x0000FF00
+#define HTERROR                         -2
+#define PWR_FAIL                        -1
+#define UNICODE_NOCHAR                  0xFFFF
+#define HTTRANSPARENT                   -1
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1000
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/src/satellite.cpp b/src/satellite.cpp
index 3b998c9..e18a6f8 100644
--- a/src/satellite.cpp
+++ b/src/satellite.cpp
@@ -3,78 +3,60 @@
 #include "beam.h"
 
 
-SATELLITE::SATELLITE(GLOBALDATA *global, ENVIRONMENT *env):_global(global),_env(env)
-{ }
-
-SATELLITE::~SATELLITE()
+SATELLITE::SATELLITE() :
+	x(env.screenWidth / 2)
 {
-  _env    = NULL;
-  _global = NULL;
+	prev_x = x;
 }
 
 
-void SATELLITE::Init()
+void SATELLITE::draw()
 {
-  x = _global->screenWidth / 2;
-  previous_x = x;
-  y = 35;
-  xv = -2;
+	drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
+	draw_sprite(global.canvas, env.misc[SATELLITE_IMAGE], x, y);
+	global.make_update(x - 20, y, 80, 60);
+	global.make_update(prev_x, y, 80, 60);
 }
 
 
-void SATELLITE::Move()
+void SATELLITE::move()
 {
-  if (x < -5)
-    xv += 1;
-  else if (x > (_global->screenWidth - 10) )
-    xv -= 1;
-
-  previous_x = x;
-  x += xv;
+	// Be sure an owned beam is valid
+	if (beam && beam->destroy)
+		beam = nullptr;
+
+	// reverse movement if the satellite reaches the screen borders
+	if (x < -5)
+		xv += 1;
+	else if (x > (env.screenWidth - 20) )
+		xv -= 1;
+
+	prev_x = x;
+	x += xv;
+
+	// If the satellite is firing, move the beam
+	if (beam)
+		beam->moveStart(xv < 0 ? x + 10: x + 40, y + 20);
 }
 
 
-
-void SATELLITE::Draw(BITMAP *dest)
+void SATELLITE::shoot()
 {
-  drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
-  draw_sprite(dest, (BITMAP *) _global->misc[SATELLITE_IMAGE],
-              x, y);
-
-  _env->make_update(x - 20, y, 80, 60);
-  _env->make_update(previous_x, y, 80, 60);
-}
-
-
-
-void SATELLITE::Shoot()
-{
-  int chance;
-  BEAM *my_beam;
-  int angle, laser_type;
-
-  if (_env->satellite == LASER_NONE)
-    return;
-
-  if (_env->naturals_since_last_shot >= 3)
-    return;
-
-  chance = rand() % 100;
-  if (! chance)        // !% chance to fire
-    {
-      if (_env->satellite == LASER_WEAK) laser_type = SML_LAZER;
-      else if (_env->satellite == LASER_STRONG) laser_type = MED_LAZER;
-      else if (_env->satellite == LASER_SUPER) laser_type = LRG_LAZER;
-      else return;
-
-      angle = rand() % 30;
-      my_beam = new BEAM(_global, _env, (xv < 0) ? x + 10: x + 40,
-                         y + 20, angle, laser_type);
-      if (! my_beam)
-        return;
-      my_beam->player = NULL;
-      _env->naturals_since_last_shot++;
-    }
+	if ( (SL_NONE != env.satellite)
+	  && (global.naturals_activated < 4)
+	  && (nullptr == beam)
+		// 1% chance to fire
+	  && (!(rand() % 100)) ) {
+		try {
+			beam = new BEAM(nullptr, xv < 0 ? x + 10: x + 40, y + 20,
+							rand() % 35 + (xv < 0 ? 320 : 5),
+							SML_LAZER + (rand() % env.satellite),
+							BT_NATURAL);
+			global.naturals_activated++;
+		} catch (...) {
+			// No problem... fire another time. ;)
+		}
+	}
 }
 
 
diff --git a/src/satellite.h b/src/satellite.h
index 3c53829..d273dae 100644
--- a/src/satellite.h
+++ b/src/satellite.h
@@ -4,28 +4,50 @@
 
 #include "environment.h"
 #include "globaldata.h"
-#include "virtobj.h"
 
 #define SATELLITE_IMAGE 16
 #define CHANCE_TO_SHOOT 100
 
+#ifndef BEAM_DEFINE
+class BEAM;
+#endif // BEAM_DEFINE
 
 class SATELLITE
-  {
-  public:
-    int x, y;
-    int xv, previous_x;
-    GLOBALDATA *_global;
-    ENVIRONMENT *_env;
-
-    SATELLITE(GLOBALDATA *global, ENVIRONMENT *env);
-    ~SATELLITE();
-    void Init();
-    void Move();
-    void Draw(BITMAP *dest);
-    void Shoot();
-
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit SATELLITE();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void draw();
+	void move();
+	void shoot();
+
+
+private:
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	BEAM*   beam   = nullptr;
+	int32_t x      = 0;
+	int32_t y      = MENUHEIGHT + 5;
+	int32_t xv     = -2;
+	int32_t prev_x = 0;
+
+};
 
 #endif
 
diff --git a/src/shop.cpp b/src/shop.cpp
new file mode 100644
index 0000000..4907894
--- /dev/null
+++ b/src/shop.cpp
@@ -0,0 +1,1002 @@
+#include "shop.h"
+#include "player.h"
+#include "files.h"
+#include "gameloop.h" // For the LevelCreator declaration.
+#include "text.h" // for draw_text_in_box()
+
+#define SHOP_BAR_HEIGHT 29
+
+
+/// ==== helper functions ====
+static int32_t calcPotentialDmg (int32_t weapNum);
+static void    divide_team_money();
+static void    do_ai_shopping   (PLAYER* pl, int32_t maxBoost, int32_t maxScore);
+static void    draw_shop        (PLAYER* pl);
+static void    draw_weapon_list (PLAYER* pl, int32_t* trolley,
+                                 int32_t scroll_old, int32_t scroll_new,
+                                 int32_t over_old, int32_t over_new);
+
+/// ==== Helper values ====
+static int32_t btps = 0;
+
+
+/// ==== External functions used ====
+void draw_simple_bg(bool drawImage);
+
+bool shop(LevelCreator* lvl_creator)
+{
+	bool    performed_save_game = false;
+	char    buf[50]             = { 0 };
+	char    description[1024]   = { 0x20, 0x0 };
+	const
+	int32_t scrollArrowPos      = env.screenWidth - STUFF_BAR_WIDTH - 30;
+
+	draw_shop (nullptr);
+
+	// Determine btps:
+	btps = ROUNDu((env.screenHeight - SHOP_BAR_HEIGHT) / STUFF_BAR_HEIGHT);
+
+	// Init global for drawing the shop:
+	global.do_updates();
+	global.stopwindow = true;
+
+	// before we do anything else, put a cap on money
+	for (int32_t z = 0; z < env.numGamePlayers; ++z) {
+		if (env.players[z]->money > 1000000000)
+			env.players[z]->money = 1000000000;
+		if (env.players[z]->money < 0)
+			env.players[z]->money = 0;
+	}
+
+
+	if (env.isGameLoaded)
+		// after the first shopping loop the game isn't fresh any more
+		env.isGameLoaded = false;
+	else
+		// Money dividing within the non-neutral teams only happens if no game
+		// was loaded. Game saving is done after that rounds money dividing.
+		divide_team_money();
+
+
+	// Determine maximum boost value and score
+	int32_t maxBoost = 0;
+	int32_t maxScore = 0;
+	for (int32_t z = 0; z < env.numGamePlayers;++z) {
+		int32_t boostValue = env.players[z]->getBoostValue();
+		if (boostValue > maxBoost)
+			maxBoost = boostValue;
+		if (env.players[z]->score > maxScore)
+			maxScore = env.players[z]->score;
+	}
+
+	// If this is demo mode, raise the max boost level, as there
+	// are no human players to define a maximum value
+	if (global.demo_mode)
+		maxBoost += env.rounds - global.currentround;
+
+	// Loop all players to let them do their shopping
+	for (int32_t pl = 0; pl < env.numGamePlayers; pl++) {
+		// computer players have their own function for their shopping
+		if ( HUMAN_PLAYER != env.players[pl]->type ) {
+			do_ai_shopping(env.players[pl], maxBoost, maxScore);
+			continue; // next one.
+		}
+
+		// Be sure no input from previous human players or from pressing
+		// the "Play" button on the player selection screen carry over:
+		flush_inputs();
+
+		int32_t money           = env.players[pl]->money;
+		int32_t trolley[THINGS] = { 0 };
+		bool    done            = false;
+		bool    need_draw       = false;
+		int32_t scroll          = 1;
+		int32_t scroll_old      = -1;
+		int32_t pressed         = -1;
+		int32_t prev_wheel      = mouse_z;
+		int32_t curr_wheel      = 0;
+		int32_t lastMouse_b     = 0;
+		int32_t lastMouse_x     = 0;
+		int32_t lastMouse_y     = 0;
+		int32_t lb              = 0;
+		int32_t itemindex       = 1;
+		int32_t hoverOver       = -1;
+		int32_t hoverOver_old   = -1;
+		int32_t cost, amt, inInv; // short cuts
+		BOX     area(20, 60, 300, 400);
+
+		env.mouseclock = 0;
+
+		draw_shop (env.players[pl]);
+
+		while (!done) {
+			while (!done && !need_draw) {
+				if (global.isCloseBtnPressed()) {
+					done = true;
+					continue;
+				}
+
+				if ( (lastMouse_x != mouse_x) || (lastMouse_y != mouse_y) ) {
+					lastMouse_x = mouse_x;
+					lastMouse_y = mouse_y;
+					if (!env.osMouse)
+						need_draw = true;
+				}
+
+				int32_t newlyOver = -1;
+
+				// Check mouse button
+				if (!lb && (mouse_b & 1)) {
+					// Check close shop button:
+					if ( (mouse_x >= (env.halfWidth - 100))
+					  && (mouse_x <  (env.halfWidth + 100))
+					  && (mouse_y >= (env.screenHeight - 50))
+					  && (mouse_y <  (env.screenHeight - 25)) )
+						done = true;
+					env.mouseclock = 0;
+				}
+				lb = (mouse_b & 1) ? 1 : 0;
+
+				/* ========================
+				 * === Keyboard control ===
+				 * ========================
+				 */
+				if ( keypressed() ) {
+					k = readkey();
+					K = k >> 8;
+				} else
+					k = K = 0;
+
+				// Move up the list
+				if  ( (K == KEY_UP) || (K == KEY_W) ) {
+					if (itemindex > 1)
+						itemindex--;
+					if (itemindex < scroll)
+						scroll = itemindex;
+					need_draw = true;
+				} else if ( (K == KEY_PGUP) || (K == KEY_R) ) {
+					itemindex -= btps;
+					if (itemindex < 1)
+						itemindex = 1;
+					if (itemindex < scroll)
+						scroll = itemindex;
+					need_draw = true;
+				}
+
+				// Move down the list
+				else if ( (K == KEY_DOWN) || (K == KEY_S) ) {
+					if (itemindex < (env.numAvailable - 1))
+						itemindex++;
+					if ( (itemindex - scroll) >= btps )
+						scroll = itemindex - (btps - 1);
+					need_draw = true;
+				} else if ( ( (K == KEY_PGDN) || (K == KEY_F) )
+				         && (scroll <= (env.numAvailable - btps) ) ) {
+					itemindex += btps;
+					if (itemindex > env.numAvailable - 1)
+						itemindex = env.numAvailable - 1;
+					if ( (itemindex - scroll) >= btps)
+						scroll = itemindex - (btps - 1);
+					need_draw = true;
+				}
+
+				// make sure the selected item is on the visible screen
+				if (itemindex < scroll)
+					itemindex = scroll;
+				else if ( itemindex >= (scroll + btps) )
+					itemindex = scroll + btps - 1;
+
+				// buy or sell an item
+				if ( (K == KEY_RIGHT) || (K == KEY_D) ) {
+					pressed = env.availableItems[itemindex];
+					if (pressed >= WEAPONS) {
+						cost  = item[pressed - WEAPONS].cost;
+						amt   = item[pressed - WEAPONS].amt;
+						inInv = env.players[pl]->ni[pressed - WEAPONS];
+					} else {
+						cost  = weapon[pressed].cost;
+						amt   = weapon[pressed].amt;
+						inInv = env.players[pl]->nm[pressed];
+					}
+
+					if (key[KEY_LCONTROL] || key[KEY_RCONTROL]) {
+						cost *= 10;
+						amt  *= 10;
+					}
+
+					if ( (money >= cost)
+					  && ( (inInv + trolley[pressed]) < (MAX_ITEMS_IN_STOCK - amt)) ) {
+						if (trolley[pressed] <= -amt) {
+							if (env.sellpercent > 0.01) {
+								money -= ROUNDu(cost * env.sellpercent);
+								trolley[pressed] += amt;
+								need_draw = true;
+							}
+						} else {
+							money -= cost;
+							trolley[pressed] += amt;
+							need_draw = true;
+							if (inInv + trolley[pressed] > MAX_ITEMS_IN_STOCK)
+								trolley[pressed] = MAX_ITEMS_IN_STOCK;
+						}
+					}
+					pressed = -1;
+				} // end of buying
+
+				else if ( (K == KEY_LEFT) || (K == KEY_A) ) {
+					pressed = env.availableItems[itemindex];
+					if (pressed >= WEAPONS) {
+						cost  = item[pressed - WEAPONS].cost;
+						amt   = item[pressed - WEAPONS].amt;
+						inInv = env.players[pl]->ni[pressed - WEAPONS];
+					} else {
+						cost  = weapon[pressed].cost;
+						amt   = weapon[pressed].amt;
+						inInv = env.players[pl]->nm[pressed];
+					}
+
+					if (key[KEY_LCONTROL] || key[KEY_RCONTROL]) {
+						cost *= 10;
+						amt  *= 10;
+					}
+
+					if (inInv + trolley[pressed] >= amt) {
+						if (trolley[pressed] >= amt) {
+							money += cost;
+							trolley[pressed] -= amt;
+							need_draw = true;
+						} else {
+							if (env.sellpercent > 0.01) {
+								money += ROUNDu(cost * env.sellpercent);
+								trolley[pressed] -= amt;
+								need_draw = true;
+							}
+						}
+					}
+					pressed = -1;
+				} // end of selling
+
+
+				// check for adding or removing rounds
+				else if ( (K == KEY_PLUS_PAD) || (K == KEY_EQUALS) ) {
+					if ( (env.rounds < MAX_ROUNDS) && (! env.mouseclock) ) {
+						env.rounds++;
+						global.currentround++;
+						need_draw = true;
+					}
+				} else if ( (K == KEY_MINUS_PAD) || (K == KEY_MINUS) ) {
+					if ( (env.rounds > 1)
+					  && (global.currentround > 1)
+					  && (! env.mouseclock) ) {
+						env.rounds--;
+						global.currentround--;
+						need_draw = true;
+					}
+				}
+
+				// check for saving the game
+				else if ( K == KEY_F10 ) {
+					if (!performed_save_game
+					  && Save_Game() )
+						performed_save_game = true;
+					if (performed_save_game)
+						snprintf(description, 64,
+						         "%s \"%s\".",
+						         env.ingame->Get_Line(17),
+						         env.game_name);
+					else
+						strncpy(description, env.ingame->Get_Line(41), 1023);
+					draw_text_in_box (&area, description, true);
+					need_draw = true;
+				}
+
+				// Keyboard exit shop:
+				if (K == KEY_ENTER)
+					done = true;
+
+				/* ========================
+				 * === Mouse control    ===
+				 * ========================
+				 */
+
+				// check mouse wheel
+				curr_wheel = mouse_z;
+				if (curr_wheel < prev_wheel) {
+					if (++scroll >= (env.numAvailable - btps) )
+						scroll = env.numAvailable - btps;
+					if (scroll > itemindex)
+						itemindex = scroll;
+					need_draw = true;
+				} else if (curr_wheel > prev_wheel) {
+					if (--scroll < 1)
+						scroll = 1;
+					if (itemindex >= (scroll + btps) )
+						itemindex = scroll + btps - 1;
+					need_draw = true;
+				}
+				prev_wheel = curr_wheel;
+
+				// Ensure the description shows what is selected
+				newlyOver = env.availableItems[itemindex];
+
+				// check mouse over items
+				if ( (mouse_x >= (env.screenWidth - STUFF_BAR_WIDTH))
+				  && (mouse_x <   env.screenWidth) ) {
+					bool    isOver    = false;
+					int32_t zzz       = scroll;
+
+					for (int32_t z = 1; (z <= btps) && !isOver; ++z) {
+						if ( (mouse_y >= (  z * STUFF_BAR_HEIGHT))
+						  && (mouse_y <  ( (z * STUFF_BAR_HEIGHT) + 30) ) )
+							isOver = true;
+						else
+							++zzz;
+					}
+
+					if (isOver && (hoverOver != env.availableItems[zzz])) {
+						newlyOver = env.availableItems[zzz];
+						itemindex = zzz;
+						need_draw = true;
+					}
+				} // End of mouse_x in stuff bar
+
+				// Switch description if necessary
+				if (hoverOver != newlyOver) {
+					if (newlyOver > -1) {
+						if (newlyOver < WEAPONS) {
+							WEAPON *weap = &weapon[newlyOver];
+							snprintf (description, 1023,
+									  "Radius: %d\nYield: %d\n\n%s",
+									  weap->radius,
+									  calcPotentialDmg (newlyOver) * weap->spread,
+									  weap->getDesc());
+						} else {
+							int32_t itemNum = newlyOver - WEAPONS;
+							ITEM *it = &item[itemNum];
+							if ( (itemNum >= ITEM_VENGEANCE)
+							  && (itemNum <= ITEM_FATAL_FURY) ) {
+								double potDmg = calcPotentialDmg(it->vals[0])
+											  * it->vals[1];
+								snprintf (description, 1023,
+										  "Potential Damage: %d\n\n%s",
+										  ROUND(potDmg), it->getDesc());
+							} else
+								snprintf (description, 1023, "%s",
+										  it->getDesc());
+						}
+					} else
+						description[0] = 0;
+					hoverOver     = newlyOver;
+					need_draw     = true;
+
+					draw_text_in_box (&area, description, true);
+				} // end of hovering on a different item
+
+				// Check mouse buttons against scrolling, buying and selling.
+				if ( (mouse_b & 1) && !env.mouseclock) {
+					if ( (mouse_x >=  scrollArrowPos)
+					  && (mouse_x <  (scrollArrowPos + 24)) ) {
+
+						// Fast up
+						if ( (mouse_y >= (env.halfHeight - 50))
+						  && (mouse_y <  (env.halfHeight - 25))
+						  && (scroll > 1) ) {
+							scroll -= btps / 2;
+							if (scroll < 1)
+								scroll = 1;
+							need_draw = true;
+						}
+
+						// Up one item
+						if ( (mouse_y >= (env.halfHeight - 24))
+						  && (mouse_y <   env.halfHeight)
+						  && (scroll > 1) ) {
+							--scroll;
+							need_draw = true;
+						}
+
+						// Down one item
+						if ( (mouse_y >= (env.halfHeight + 1))
+						  && (mouse_y <  (env.halfHeight + 25))
+						  && (scroll  <  (env.numAvailable - btps)) ) {
+							++scroll;
+							need_draw = true;
+						}
+
+						// Fast down
+						if ( (mouse_y >= (env.halfHeight + 25))
+						  && (mouse_y <  (env.halfHeight + 50))
+						  && (scroll  <  (env.numAvailable)) ) {
+							scroll += btps / 2;
+							if (scroll >= env.numAvailable - btps)
+								scroll = env.numAvailable - btps;
+							need_draw = true;
+						}
+					}
+					if (itemindex < scroll)
+						itemindex = scroll;
+					else if ( itemindex > (scroll + btps) )
+						itemindex = scroll + btps - 1;
+                }
+
+                // Check mouse buttons for clicks
+				if ( ( (mouse_b & 1) || (mouse_b & 2) )
+				  && (mouse_x >= (env.screenWidth - STUFF_BAR_WIDTH))
+				  && (mouse_x <   env.screenWidth) )
+					pressed = env.availableItems[itemindex];
+
+				// Only do the buying / selling when the mouse button is
+				// released. This way users can pull the mouse off the item
+				if ( (pressed > -1) && !((mouse_b & 1) || (mouse_b & 2)) ) {
+					if (pressed < WEAPONS) {
+						cost  = weapon[pressed].cost;
+						amt   = weapon[pressed].amt;
+						inInv = env.players[pl]->nm[pressed];
+					} else {
+						cost  = item[pressed - WEAPONS].cost;
+						amt   = item[pressed - WEAPONS].amt;
+						inInv = env.players[pl]->ni[pressed - WEAPONS];
+					}
+
+					if (key[KEY_LCONTROL] || key[KEY_RCONTROL]) {
+						cost *= 10;
+						amt  *= 10;
+					}
+
+					// RMB sells items and takes precedence over LMB
+					if (lastMouse_b & 2) {
+						if ( (inInv + trolley[pressed]) >= amt) {
+							if (trolley[pressed] >= amt) {
+								money += cost;
+								trolley[pressed] -= amt;
+								need_draw = true;
+							} else if (env.sellpercent > 0.01) {
+								money += ROUNDu(cost * env.sellpercent);
+								trolley[pressed] -= amt;
+								need_draw = true;
+							}
+						}
+					} else if ( (money >= cost)
+						     && ( (inInv + trolley[pressed])
+					            < (MAX_ITEMS_IN_STOCK - amt)) ) {
+						if (trolley[pressed] <= -amt) {
+							if (env.sellpercent > 0.01) {
+								money -= ROUNDu(cost * env.sellpercent);
+								trolley[pressed] += amt;
+								need_draw = true;
+							}
+						} else {
+							money -= cost;
+							trolley[pressed] += amt;
+							need_draw = true;
+							if ( (inInv + trolley[pressed]) > MAX_ITEMS_IN_STOCK)
+								trolley[pressed] = MAX_ITEMS_IN_STOCK;
+						}
+					}
+					pressed = -1;
+				} // End of mouse buttons released with item selected
+				env.mouseclock++;
+				if (env.mouseclock > 5)
+					env.mouseclock = 0;
+				lastMouse_b = mouse_b;
+
+				// Sleep a bit if nothing happened
+				if (!done && !need_draw)
+					LINUX_SLEEP
+			} // End of input handling loop
+
+			// Update display if anything happened:
+			if (need_draw) {
+				need_draw = false;
+
+				// No hardware mouse while drawing
+				SHOW_MOUSE(nullptr)
+
+				global.make_update(env.halfWidth - 200, 0,
+				                   env.gfxData.stuff_bar[0]->w,
+				                   env.gfxData.stuff_bar[0]->h);
+
+				draw_sprite (global.canvas, env.gfxData.stuff_bar[0],
+							 env.halfWidth - 200, 0);
+				textprintf_ex (global.canvas, font, env.halfWidth - 190, 0, BLACK,
+							   -1, "%s %d: %s", env.ingame->Get_Line(10), pl + 1,
+							   env.players[pl]->getName ());
+				textprintf_ex (global.canvas, font, env.halfWidth - 190, 14, BLACK,
+							   -1, "%s: $%s", env.ingame->Get_Line(11),
+							   Add_Comma(money));
+				snprintf (buf, 49, "%s: %d/%d", env.ingame->Get_Line(12),
+						  env.rounds - global.currentround, env.rounds);
+				textout_ex (global.canvas, font, buf,
+							env.halfWidth + 170 - text_length(font, buf),
+							0, BLACK, -1);
+				snprintf (buf, 49, "%s: %d", env.ingame->Get_Line(13),
+						  env.players[pl]->score);
+				textout_ex (global.canvas, font, buf,
+							env.halfWidth + 155 - text_length(font, buf),
+							14, BLACK, -1);
+
+				draw_weapon_list(env.players[pl], trolley, scroll_old, scroll,
+				                 hoverOver_old, hoverOver);
+
+				// Update non-OS mouse movements
+				SHOW_MOUSE(global.canvas)
+
+				global.do_updates ();
+				hoverOver_old = hoverOver;
+				scroll_old    = scroll;
+			} // End of drawing
+		} // End of player shopping loop
+
+		// Now write back bought/sold items and remaining money
+		for (int tItem = 0; tItem < WEAPONS; tItem++)
+			env.players[pl]->nm[tItem] += trolley[tItem];
+		for (int tItem = WEAPONS; tItem < THINGS; tItem++)
+			env.players[pl]->ni[tItem - WEAPONS] += trolley[tItem];
+		env.players[pl]->money = money;
+	} // End of player handling
+
+	// Eventually give all players interest on their remaining money
+	if (!global.isCloseBtnPressed()) {
+		for (int32_t z = 0; z < env.numGamePlayers; z++) {
+			int32_t money    = env.players[z]->money;
+			int32_t interest = 0;
+			double  intPerc  = .0;
+			int32_t intLevel = 0;
+			int32_t intSum   = 0; // The summed up interest
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "======================================================", 0)
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "%2d.: %s enters the bank to get interest:", (z+1),
+			              env.players[z]->getName())
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "     Starting Account: %10d", env.players[z]->money)
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "------------------------------------------------------", 0)
+			while (money && (intLevel++ < 5)) {
+				// Enter next level
+				intPerc  = (env.interest - 1.0) / intLevel;
+				interest = static_cast<double>(money) * intPerc;
+
+				// The limit is only applicable on the first four levels,
+				// in the fifth level interest is fully applied!
+				if ((interest > MAX_INTEREST_AMOUNT) && (intLevel < 5))
+					interest = MAX_INTEREST_AMOUNT;
+
+				// Now sum the interest up and substract the counted money!
+				intSum += interest;
+				money  -= static_cast<double>(interest) / intPerc;
+
+				DEBUG_LOG_FIN(env.players[z]->getName(),
+				              "     Level %1d:  %8d credits are rated,",
+				              intLevel,
+				              static_cast<int32_t>(interest / intPerc))
+				DEBUG_LOG_FIN(env.players[z]->getName(),
+				              "     Interest: %8d credits. (%5.2f%%)",
+				              interest, intPerc * 100.)
+
+				// To get rid of (possible) rounding errors, add a security check:
+				if ( (money < (4 * intLevel)) || (interest < 1) )
+					money = 0; // With less there won't be any more interest anyway!
+
+				DEBUG_LOG_FIN(env.players[z]->getName(),
+				              "     Unrated : %8d credits left.", money)
+			}
+
+			// Now give them their money:
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "     Sum:      %8d credits.", intSum)
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "------------------------------------------------------", 0)
+			env.players[z]->money += intSum;
+			DEBUG_LOG_FIN(env.players[z]->getName(),
+			              "     Final Account   : %10d", env.players[z]->money)
+		} // End of looping players
+	} // End of close button not pressed
+
+
+	// If the close button was pressed, the creator thread must end
+	// ASAP so we can get out of here.
+	if (global.isCloseBtnPressed())
+		lvl_creator->die_now();
+
+	// The LevelCreator, if not finished, can work alone, now:
+	if (!lvl_creator->is_finished())
+		lvl_creator->work_alone();
+
+	// Wait until the level creator is done
+	while (!lvl_creator->is_finished()) {
+		MSLEEP(20)
+		if ( lvl_creator->has_progress() ) {
+			// Hide custom mouse pointer
+			SHOW_MOUSE(nullptr)
+
+			lvl_creator->print_state();
+
+			// Draw custom mouse cursor
+			SHOW_MOUSE(global.canvas)
+
+			global.do_updates();
+		}
+	}
+
+	return !global.isCloseBtnPressed();
+}
+
+
+/*
+ *  Calculate the potential damage for a given weapon.
+ *  Recursively add the damage of sub-munitions.
+ */
+static int32_t calcPotentialDmg (int32_t weapNum)
+{
+	WEAPON *weap = &weapon[weapNum];
+	int32_t total = 0;
+
+	if ( (weap->submunition >= 0) && (weap->numSubmunitions > 0) )
+		total += calcPotentialDmg (weap->submunition)
+		       * weap->numSubmunitions;
+	else
+		total += weap->damage;
+
+	return total;
+}
+
+
+// If configured to do so, this method divides team money to help out team mates
+static void divide_team_money()
+{
+	if (!env.divide_money)
+		return;
+
+	int32_t jediMoney = 0;
+	int32_t jediCount = 0;
+	int32_t sithMoney = 0;
+	int32_t sithCount = 0;
+	int32_t teamFee   = 0;
+
+	for (int32_t z = 0; z < env.numGamePlayers; ++z) {
+		// Sum up team money:
+		if (env.players[z]->team == TEAM_JEDI) {
+			teamFee = static_cast<double>(env.players[z]->money) / 4.;
+			if (teamFee > MAX_TEAM_AMOUNT)
+				teamFee = MAX_TEAM_AMOUNT;
+			jediMoney += teamFee;
+			jediCount++;
+		} else  if (env.players[z]->team == TEAM_SITH) {
+			teamFee = static_cast<double>(env.players[z]->money) / 4;
+			if (teamFee > MAX_TEAM_AMOUNT)
+				teamFee = MAX_TEAM_AMOUNT;
+			sithMoney += teamFee;
+			sithCount++;
+		}
+		// Note: The team Fee is not docked, yet, as it is not clear
+		// whether there is more than one team member.
+	}
+
+	DEBUG_LOG_FIN("Overview", "Jedi Count: %d - Sith Count: %d", jediCount, sithCount)
+
+	// Now apply the team money (if any):
+	if (jediCount > 1) {
+		DEBUG_LOG_FIN("Overview",
+					  "The Jedi summed up a pool of %13d credits!",
+					  jediMoney)
+		jediMoney = static_cast<double>(jediMoney) * .90 / jediCount;
+		DEBUG_LOG_FIN("Overview",
+					  "Every Jedi will receive %10d credits out of the pool!",
+					  jediMoney)
+		for (int32_t z = 0; z < env.numGamePlayers; ++z) {
+			if (TEAM_JEDI == env.players[z]->team) {
+				teamFee = static_cast<double>(env.players[z]->money) / 4;
+				if (teamFee > MAX_TEAM_AMOUNT)
+					teamFee = MAX_TEAM_AMOUNT;
+				env.players[z]->money -= teamFee;
+				env.players[z]->money += jediMoney;
+			}
+		}
+	}
+
+	if (sithCount > 1) {
+		DEBUG_LOG_FIN("Overview",
+					  "The Sith summed up a pool of %13d credits!",
+					  sithMoney)
+		sithMoney = static_cast<double>(sithMoney) * .90 / sithCount;
+		DEBUG_LOG_FIN("Overview",
+					  "Every Sith will receive %10d credits out of the pool!",
+					  sithMoney)
+		for (int32_t z = 0; z < env.numGamePlayers; ++z) {
+			if (TEAM_SITH == env.players[z]->team) {
+				teamFee = static_cast<double>(env.players[z]->money) / 4;
+				if (teamFee > MAX_TEAM_AMOUNT)
+					teamFee = MAX_TEAM_AMOUNT;
+				env.players[z]->money -= teamFee;
+				env.players[z]->money += sithMoney;
+			}
+		}
+	}
+}
+
+
+/// @brief dedicated function for AI shopping.
+void do_ai_shopping(PLAYER* player, int32_t maxBoost, int32_t maxScore)
+{
+	// Print player info and inventory
+#ifdef ATANKS_DEBUG_FINANCE
+	DEBUG_LOG_FIN(player->getName(),
+	              "Starting to buy: (Defensiveness: %4.2lf)",
+	              player->defensive)
+
+
+	DEBUG_LOG_FIN(player->getName(), " --- Inventory --- ", 0)
+	DEBUG_LOG_FIN(player->getName(), "-------------------", 0)
+	for (int32_t i = 1; i < WEAPONS; ++i) {
+		if (player->nm[i])
+			DEBUG_LOG_FIN(player->getName(), "% 4d x %s",
+			              player->nm[i] / weapon[i].getDelayDiv(),
+			              weapon[i].getName())
+	}
+	DEBUG_LOG_FIN(player->getName(), " - - - - - - - - - ", 0)
+	for (int32_t i = 1; i < ITEMS; ++i) {
+		if (player->ni[i])
+			DEBUG_LOG_FIN(player->getName(), "% 4d x %s",
+			              player->ni[i], item[i].getName())
+	}
+	DEBUG_LOG_FIN(player->getName(), "-------------------", 0)
+
+	int32_t oldMoneyToSave = -1; // So the same message isn't repeated over and over again.
+#endif // ATANKS_DEBUG_FINANCE
+
+	player->updatePreferences(maxBoost, maxScore);
+
+	// money saving will be made possible when:
+	// 1. It's not the first three rounds
+	// 2. It's not the last 5 rounds
+	// and, dynamically:
+	// 3. We have at least 10 parachutes or no gravity
+	// 4. We have at least 2 damage dealing (not small missile) weapon
+	//    per AI level.
+
+	// Check for a minimum of damage dealing weapons and parachutes,
+	// then buy until 'moneyToSave' is reached.
+	int32_t pressed      = -1;
+	int32_t ai_level     = static_cast<int32_t>(player->type);
+	int32_t buy_count    = 0;
+	int32_t last_buy_idx = 0; // Used to "remember" where the AI was in its cart.
+	do {
+		int32_t moneyToSave = 0; // How much money will the player save?
+
+		// The AI does not save up money in the first three or last five rounds
+		if ( (global.currentround > 5)
+		  && ((env.rounds - global.currentround) > 3) ) {
+			moneyToSave = player->getMoneyToSave(!buy_count);
+#ifdef ATANKS_DEBUG_FINANCE
+			if (oldMoneyToSave != moneyToSave) {
+				DEBUG_LOG_FIN(player->getName(),
+				              "Maximum Money to save: %d (I have %d)",
+				              moneyToSave, player->money)
+				oldMoneyToSave = moneyToSave;
+			}
+		} else
+			DEBUG_LOG_FIN(player->getName(),
+			              "No money to save this round!", 0);
+#else
+		}
+#endif // ATANKS_DEBUG_FINANCE
+
+		int32_t numPara     = player->ni[ITEM_PARACHUTE];
+		int32_t numDmgWeaps = 0;
+
+		for (int32_t i = 1; i < WEAPONS; ++i) {
+			// start from 1, as 0 is the small missile
+			if (weapon[i].damage > 0)
+				numDmgWeaps += player->nm[i] / weapon[i].getDelayDiv();
+		}
+
+		// Try to chose something to buy if enough money is there or either
+		// the number of parachutes or damage dealing weapons is too low.
+		if ( (player->money > moneyToSave)
+		  || ( (numPara   < ai_level) && (env.landSlideType > SLIDE_NONE) )
+		  || (numDmgWeaps <  (ai_level * 2)) )
+			pressed = player->chooseItemToBuy(maxBoost, last_buy_idx);
+		else
+			pressed = -1; // Forced to end.
+
+		DEBUG_LOG_FIN(player->getName(),
+		              "I have %s%s%s%d credits left%s",
+		              pressed > -1 ? "bought: " : "finished, with ",
+		              pressed > -1
+		              ? pressed < WEAPONS
+		                ? weapon[pressed].getName()
+		                : item[pressed - WEAPONS].getName()
+		              : "",
+		              pressed < 0 ? " " : " (",
+		              player->money,
+		              pressed < 0 ? "" : ")")
+		buy_count++;
+	} while ( (pressed != -1) && (buy_count < 1000) );
+
+	DEBUG_LOG_FIN(player->getName(),
+	              "============================================", 0)
+}
+
+
+static void draw_shop(PLAYER *pl)
+{
+	global.make_update (0, 0, env.screenWidth, env.screenHeight);
+	global.lockLand();
+	SHOW_MOUSE(nullptr)
+	draw_simple_bg(false);
+
+	if (pl) {
+		draw_sprite (global.canvas, env.misc[DONE_IMAGE],
+					 env.halfWidth - 100, env.screenHeight - 50);
+		draw_sprite (global.canvas, env.misc[FAST_UP_ARROW_IMAGE],
+					 env.screenWidth - STUFF_BAR_WIDTH - 30, env.halfHeight - 50);
+		draw_sprite (global.canvas, env.misc[UP_ARROW_IMAGE],
+					 env.screenWidth - STUFF_BAR_WIDTH - 30, env.halfHeight - 25);
+		draw_sprite (global.canvas, env.misc[DOWN_ARROW_IMAGE],
+					 env.screenWidth - STUFF_BAR_WIDTH - 30, env.halfHeight);
+		draw_sprite (global.canvas, env.misc[FAST_DOWN_ARROW_IMAGE],
+					 env.screenWidth - STUFF_BAR_WIDTH - 30, env.halfHeight + 25);
+	}
+
+	drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
+	global.current_drawing_mode = DRAW_MODE_TRANS;
+
+	if (pl) {
+		double  left  = env.halfWidth - 200; // short cut
+		int32_t right = env.screenWidth - 1; // another short cut.
+
+		for (int32_t z = 0; z < env.halfWidth - 200; z++) {
+			set_trans_blender (0, 0, 0, ROUNDu(static_cast<double>(z) / left * 240) + 15);
+			vline (global.canvas, z,         0, SHOP_BAR_HEIGHT, pl->color);
+			vline (global.canvas, right - z, 0, SHOP_BAR_HEIGHT, pl->color);
+		} // End of drawing player colour blending
+	} // End of having a player
+
+	solid_mode ();
+	global.current_drawing_mode = DRAW_MODE_SOLID;
+
+	if (pl) {
+		textout_ex(global.canvas, font, env.ingame->Get_Line(14), 20, 420, WHITE, -1);
+		textout_ex(global.canvas, font, env.ingame->Get_Line(15), 20, 450, WHITE, -1);
+		textout_ex(global.canvas, font, env.ingame->Get_Line(16), 20, 465, WHITE, -1);
+	}
+
+	global.unlockLand();
+	fi = 1;
+}
+
+
+void draw_simple_bg(bool drawImage)
+{
+	if (!env.drawBackground)
+		rectfill(global.canvas, 0, 0, env.screenWidth - 1, env.screenHeight - 1, BLACK);
+	else if ( drawImage && env.misc[17] )
+		stretch_blit(env.misc[17], global.canvas, 0, 0,
+					env.misc[17]->w, env.misc[17]->h, 0, 0,
+					env.screenWidth, env.screenHeight);
+	else
+		rectfill(global.canvas, 0, 0, env.screenWidth - 1, env.screenHeight - 1, DARK_GREEN);
+}
+
+
+static void draw_weapon_list(PLAYER* pl, int32_t* trolley,
+                             int32_t scroll_old, int32_t scroll_new,
+                             int32_t over_old, int32_t over_new)
+{
+	// Some pre-calculations and settings.
+	int32_t        startX       = env.screenWidth - STUFF_BAR_WIDTH;
+	int32_t        halfBar      = STUFF_BAR_HEIGHT / 2;
+	static int32_t qtyTxtLen    = 0;
+	BITMAP*        imgReleased  = env.gfxData.stuff_bar[0];
+	BITMAP*        imgPressed   = env.gfxData.stuff_bar[1];
+	int32_t        col_add      = YELLOW;             // Bought items
+	int32_t        col_sub      = makecol(176, 0, 0); // Sold items
+	static char    buf_cost[50] = { 0 };
+	static char    buf_amt[50]  = { 0 };
+	bool           full_redraw  = (scroll_new != scroll_old);
+
+	memset(buf_cost, 0, sizeof(char) * 50);
+	memset(buf_amt,  0, sizeof(char) * 50);
+
+	if (0 == qtyTxtLen) {
+		qtyTxtLen = text_length(font, "Qty. in inventory: ddd");
+	}
+
+	// Erase top gap:
+	if (full_redraw) {
+		global.lockLand();
+		rectfill(global.canvas, startX, STUFF_BAR_HEIGHT - 5,
+				 startX + env.gfxData.stuff_icon_base->w, STUFF_BAR_HEIGHT,
+				 makecol(8, 110, 24));
+		global.unlockLand();
+		global.make_update(startX, STUFF_BAR_HEIGHT - 5,
+						   env.gfxData.stuff_icon_base->w, 5);
+	}
+
+	// go through all items and draw them on the screen with
+	// the amount of items in the trolley
+	for (int32_t slot = 1; slot <= btps; ++slot) {
+		int32_t     itemIdx = slot + scroll_new - 1;
+		int32_t     itemNum = env.availableItems[itemIdx];
+		int32_t     startY  = slot * STUFF_BAR_HEIGHT;
+		const char* name    = nullptr;
+		int32_t     amt     = 0;
+		int32_t     d_div   = 1;
+
+		// Only actually draw the slot, if it has changed:
+		if ( !full_redraw
+		  && (over_old != itemNum)
+		  && (over_new != itemNum) )
+			continue;
+
+		// Get text values:
+		if (itemNum < WEAPONS) {
+			d_div = weapon[itemNum].getDelayDiv();
+			name  = weapon[itemNum].getName();
+			amt   = pl->nm[itemNum] / d_div;
+			snprintf (buf_cost, 49, "$%s", Add_Comma( weapon[itemNum].cost ) );
+			snprintf (buf_amt,  49, "for %d", weapon[itemNum].amt / d_div);
+		} else {
+			name = item[itemNum - WEAPONS].getName();
+			amt  = pl->ni[itemNum - WEAPONS];
+			snprintf (buf_cost, 49, "$%s",
+			          Add_Comma( item[itemNum - WEAPONS].cost ) );
+			snprintf (buf_amt,  49, "for %d", item[itemNum - WEAPONS].amt);
+		}
+
+		global.lockLand();
+
+		// Draw the background sprites
+		draw_sprite(global.canvas,
+		            (over_new == itemNum) ? imgPressed : imgReleased,
+		            startX, startY);
+		draw_sprite(global.canvas, env.gfxData.stuff_icon_base, startX, startY);
+		draw_sprite(global.canvas, env.stock[itemNum], startX, startY - 5);
+		global.make_update(startX, startY, STUFF_BAR_WIDTH, STUFF_BAR_HEIGHT + 5);
+
+		// Draw the text:
+		textout_ex   (global.canvas, font, name,
+		              startX + 45, startY - 1, BLACK, -1);
+		textprintf_ex(global.canvas, font,
+		              startX + 45, startY + halfBar - 4, BLACK, -1,
+		              "%s: %d",
+		              env.ingame->Get_Line(40), amt);
+		if (trolley[itemNum])
+			textprintf_ex(global.canvas, font, startX + 45 + qtyTxtLen,
+			              startY + halfBar - 4,
+			              trolley[itemNum] > 0 ? col_add : col_sub, -1,
+			              "%+d", trolley[itemNum] / d_div);
+		textout_ex (global.canvas, font, buf_cost,
+		            env.screenWidth - 45 - text_length(font, buf_cost),
+		            startY - 1, BLACK, -1);
+		textout_ex (global.canvas, font, buf_amt,
+		            env.screenWidth - 45 - text_length(font, buf_amt),
+		            startY + halfBar - 4, BLACK, -1);
+		global.unlockLand();
+
+        // Break up if done:
+        // (This should not be triggered ever, as scroll is controlled by shop()
+		//  to never be more than env.numAvailable-btps.)
+		if ( (itemIdx >= (env.numAvailable - 1)) && (slot < btps) )
+			slot = btps + 1;
+	}
+
+	fi = 1;
+}
+
+
+/** @brief Executes a fast and simple transition from global.canvas to the screen.
+**/
+void quickChange(bool clearerror)
+{
+	if (errorMessage) {
+		textout_ex (global.canvas, font, errorMessage, errorX, errorY,
+		            makecol (255, 0, 0), -1);
+		if (clearerror)
+			errorMessage = nullptr;
+	}
+
+	blit (global.canvas, screen, 0, 0, 0, 0, env.screenWidth, env.screenHeight);
+}
+
diff --git a/src/shop.h b/src/shop.h
new file mode 100644
index 0000000..ab41c52
--- /dev/null
+++ b/src/shop.h
@@ -0,0 +1,12 @@
+#pragma once
+#ifndef ATANKS_SRC_SHOP_H_INCLUDED
+#define ATANKS_SRC_SHOP_H_INCLUDED
+
+class LevelCreator; // From gameloop.h
+
+// give people the chance to buy items
+bool shop(LevelCreator* lvl_creator);
+
+
+#endif // ATANKS_SRC_SHOP_H_INCLUDED
+
diff --git a/src/sky.cpp b/src/sky.cpp
index 685f68b..12499db 100644
--- a/src/sky.cpp
+++ b/src/sky.cpp
@@ -29,16 +29,29 @@ TODO
  + Add clouds?
 */
 
-#include "globaldata.h"
+#include "externs.h"
 #include "main.h"
 #include <vector>
 #include "environment.h"
 #include "sky.h"
 #include "files.h"
+#include "gfxData.h"
+#include "gameloop.h"
 
 
-/* Here's a function that could move nicely out of atanks.cc :) */
-int gradientColorPoint (const gradient *grad, double length, double line);
+/*****************************************************************************
+Static temp sky bitmap for faster sky creation
+*****************************************************************************/
+static BITMAP* temp_sky = nullptr;
+
+
+/*****************************************************************************
+Static helper function prototypes
+*****************************************************************************/
+static double  central_rand (double u);
+static int32_t clamped_int  (int32_t m, int32_t a, int32_t z);
+static double  coverage     (double distance, double radius);
+static void    draw_moons   (LevelCreator* lcr, int32_t width, int32_t height);
 
 
 /*============================================================================
@@ -47,66 +60,90 @@ struct moon
 A simple data structure to store the parameters of a moon for easy passing.
 ============================================================================*/
 struct moon
-  {
-    int radius;
-    int x;
-    int y;
-    double lambda;
-    int octaves;
-    double smoothness;
-    double xoffset;
-    double yoffset;
-    int col1;
-    int col2;
-  };
+{
+	BITMAP* bitmap;
+	int32_t   col1;
+	int32_t   col2;
+	double          lambda;
+	int32_t         octaves;
+	int32_t         radius;
+	double          smoothness;
+	int32_t         x;
+	double          xoffset;
+	int32_t         y;
+	double          yoffset;
+
+	// Simple ctor:
+	explicit moon(int32_t scrnw, int32_t scrnh) :
+		col1      (makecol(rand() % 255, rand() % 255, rand() % 255)),
+		col2      (makecol(rand() % 255, rand() % 255, rand() % 255)),
+		lambda    (((rand() % 60) + 30) / 100.),
+		octaves   ((rand() % 4) + 6),
+		radius    (static_cast<int32_t>(central_rand(scrnw / 8) + .5)),
+		smoothness((rand() % 20) + 3),
+		x         (rand() % scrnw),
+		xoffset   (rand()),
+		y         (rand() % scrnh),
+		yoffset   (rand())
+	{
+		bitmap = create_bitmap (radius * 2, radius * 2);
+	}
+
+	// Simple dtor to get rid of the temp bitmap
+	~moon()
+	{
+		if (bitmap)
+			destroy_bitmap(bitmap);
+	}
+};
+
 
 
 /*############################################################################
 ZBuffer
 
 Acts a a simple, 1bpp zbuffer.  For each pixel location, the ZBuffer can
-remember if something is (char *)"popping up" at that location.
+remember if something is "popping up" at that location.
 ############################################################################*/
 class ZBuffer
-  {
-  private:
-    // empty ctor, copy-ctor and assign operator are private, so the compiler won't create implicit ones!
-    inline ZBuffer () { }
-    inline ZBuffer (ZBuffer &sourceZBuf _UNUSED)_UNUSED;
-    inline const ZBuffer& operator= (const ZBuffer &sourceZBuf) { return(sourceZBuf); }
-
-    std::vector< bool >	z;
-    int shiftamt;
+{
+public:
+	// No copies:
+	ZBuffer() = delete;
+	ZBuffer& operator=(const ZBuffer&) = delete;
 
-  public:
     /*************************************************************************
     ctor
 
-    Construct a ZBuffer object capable of storing (char *)"popup" values for a
+    Construct a ZBuffer object capable of storing "popup" values for a
     w by h grid.  All cells in the ZBuffer start out lowered.
     *************************************************************************/
-    ZBuffer( int w, int h )
-    {
-      shiftamt = 0;
-      while ( w )
-        {
-          w >>= 1;
-          ++shiftamt;
-        }
-      z.resize( h << shiftamt );
-    }
+	ZBuffer( int32_t w, int32_t h )
+	{
+		int32_t width = w;
+		while ( width ) {
+			width >>= 1;
+			++shiftamt;
+		}
+		z.resize( (h << shiftamt) | w );
+	}
+
 
     /*************************************************************************
     test
 
-    Returns true iff the cell at location (x,y) is raised.  Behavior is
+    Returns true if the cell at location (x,y) is raised.  Behaviour is
     undefined if x does not fall in the range [0,w) or if y does not fall in
     the range [0,h); w and h being the parameters to the ctor.
     *************************************************************************/
-    bool test( int x, int y ) const
-      {
-        return z[ (y<<shiftamt) | x ];
-      }
+	bool test( int32_t x, int32_t y ) const
+	{
+		try {
+			return z.at((y << shiftamt) | x);
+		} catch (...) {
+			return false;
+		}
+	}
 
 
     /*************************************************************************
@@ -115,11 +152,29 @@ class ZBuffer
     Causes a cell in the ZBuffer to become raised.  Follows the same
     conditions on x and y as the test function does.
     *************************************************************************/
-    void set( int x, int y )
-    {
-      z[ (y<<shiftamt) | x ] = true ;
-    }
-  };
+	void set( int32_t x, int32_t y )
+	{
+		try {
+			z.at((y<<shiftamt) | x) = true ;
+		} catch (...) { /* nothing can be done here... */ }
+	}
+
+private:
+	std::vector< bool >	z;
+	int32_t             shiftamt = 0;
+};
+
+
+/*****************************************************************************
+Static function prototypes that need either moon or ZBuffer
+*****************************************************************************/
+static void    draw_amoon   (LevelCreator* lcr, const moon &mn,
+                             int32_t x0, int32_t y0, int32_t x1, int32_t y1,
+                             bool darkside, ZBuffer &zbuffer);
+static void    paint_moonpix(int32_t x, int32_t y,
+                             const moon &mn, double xval, double yval,
+                             double blend);
+
 
 
 /*****************************************************************************
@@ -130,10 +185,11 @@ are preferred.
 
 Basic on a simple cubic function.
 *****************************************************************************/
-static double central_rand( double u )
+static double central_rand(double u)
 {
-  const double x = ( (double)rand( ) / RAND_MAX ) - 0.5;	// [-.5,+.5]
-  return u * (0.5 - (x*x*x)*4.0) ;
+	const double x = static_cast<double>(rand()) / static_cast<double>(RAND_MAX)
+	               - 0.5; // [-.5,+.5]
+	return u * (0.5 - (x*x*x)*4.0) ;
 }
 
 
@@ -143,37 +199,9 @@ clamped_int
 Clamps an integer value, m, into a range specified by [a,z].  Returns the
 clamped value.
 *****************************************************************************/
-static int clamped_int( int m, int a, int z )
+static int32_t clamped_int(int32_t m, int32_t a, int32_t z)
 {
-  return (m<a) ? a : ( (m>z) ? z : m );
-}
-
-
-/*****************************************************************************
-generate_moon
-
-Returns a moon structure with appropriately randomized variables.
-*****************************************************************************/
-static moon generate_moon( int scrnw, int scrnh )
-{
-  moon m;
-
-  m.smoothness = (rand () % 20) + 3;
-  m.octaves = rand () % 4 + 6;
-  m.lambda = (rand () % 60 + 30) * (1.00/100);
-
-  //m.radius = (int)((rand () % 100 / 200.0) * (rand () % 100 / 200.0) * scrnw);
-  m.radius = (int)central_rand( scrnw/8 ) ;
-  m.x = rand () % scrnw;
-  m.y = rand () % scrnh;
-
-  m.xoffset = rand ();
-  m.yoffset = rand ();
-
-  m.col1 = makecol (rand () % 255, rand () % 255, rand () % 255);
-  m.col2 = makecol (rand () % 255, rand () % 255, rand () % 255);
-
-  return m;
+	return ( m < a ? a : ( m > z ? z : m ) );
 }
 
 
@@ -181,24 +209,89 @@ static moon generate_moon( int scrnw, int scrnh )
 coverage
 
 Compute the percent coverage of a pixel by a sphere given the pixel's
-distance from the center and the sphere's radius.
+distance from the centre and the sphere's radius.
 *****************************************************************************/
-static double coverage( double distance, double fradius )
+static double coverage(double distance, double radius)
 {
-  if ( distance <= fradius )
-    return 1.0 ;
-  return 1 - (distance - fradius);
+	if ( distance > radius )
+		return 1 - (distance - radius);
+	return 1. ;
 }
 
 
 /*****************************************************************************
-fract_clamp - unused
+draw_amoon
 
-Clamp a fraction down to the range [0.0,1.0]
+Renders a single moon onto a bitmap.  Assumes transparent drawing is enabled.
+Obeys the given bounding box, which may be smaller than the moon itself.
+Uses the darkside parameter to decide which side of the moon should be
+dark.  Obeys and updates the z-buffer.
+
+The current implementation of this function is begging for some
+simplifications.  And again, what about those [xy]offset variables?
 *****************************************************************************/
-/*static double fract_clamp( double x ) {
-	return (x < 0.0) ? 0.0 : ( (x>1.0) ? 1.0 : x );
-}*/
+static void draw_amoon(LevelCreator* lcr, const moon& mn,
+                       int32_t x0, int32_t y0, int32_t x1, int32_t y1,
+                       bool darkside, ZBuffer& zbuffer)
+{
+	int32_t startX = std::min(x0, x1);
+	int32_t endX   = std::max(x0, x1);
+	int32_t startY = std::min(y0, y1);
+	int32_t endY   = std::max(y0, y1);
+
+	clear_to_color(mn.bitmap, BLACK);
+	blit(temp_sky, mn.bitmap, startX, startY, 0, 0, mn.radius * 2, mn.radius * 2);
+
+	for (int32_t y = startY; (y < endY) && lcr->can_work(); ++y ) {
+		bool hityet = false;
+
+		for (int32_t x = startX; (x < endX) && lcr->can_work(); ++x ) {
+			/* Occupied? */
+			if ( zbuffer.test(x,y) )
+				continue ;
+
+			/* Find distance from this moon */
+			int32_t xdist = mn.x - x;
+			int32_t ydist = mn.y - y;
+
+			/* Compute some other nice circle values */
+			const
+			double radius    = mn.radius ;
+			double xval      = static_cast<double>(xdist) / radius;
+			double yval      = static_cast<double>(ydist) / radius;
+			double distance2 = (xdist * xdist) + (ydist * ydist);
+			double distance  = std::sqrt(distance2);
+
+			/* A bound check -> are we in the circle? */
+			if ( distance > (radius + 1) ) {
+				if (hityet) // If we've already been inside at this y...
+					break;  // then skip ahead to the next y
+				continue ;  // Otherwise stay at this y, and skip to the next x
+			}
+
+			/* Edges use lighter blending */
+			const double edgeval = coverage(distance, radius);
+
+			/* Now, should we paint this side of the moon? */
+			if (xval && ( (xval < 0) == darkside) ) {
+				lcr->yield();
+				paint_moonpix(x - startX, y - startY, mn, fabs(xval), yval, edgeval);
+			}
+
+			/* Mark this pixel as occupied */
+			zbuffer.set(x,y);
+			hityet = true ;
+		}
+	}
+
+	// Put the moon on the sky bitmap:
+	global.lockLand();
+	drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
+	blit (mn.bitmap, temp_sky, 0, 0,
+		  startX, startY, mn.radius * 2, mn.radius * 2);
+	drawing_mode(global.current_drawing_mode, NULL, 0, 0);
+	global.unlockLand();
+}
 
 
 
@@ -214,138 +307,58 @@ Parameters:
 	The moon to paint.  The val's give the percentage along the moon.
 	Percentages must fall within [0,1].
 * blend
-	Controls how much (char *)"paint" is used.  Must be in the range [0,1].  Higher
+	Controls how much "paint" is used.  Must be in the range [0,1].  Higher
 	values cause stronger painting.  Used for anti-aliasing.
 
 *****************************************************************************/
-static void paint_moonpix( BITMAP* bmp, int x, int y,
-                           const moon& mn, double xval, double yval,
-                           double blend )
+static void paint_moonpix(int32_t x, int32_t y,
+                          const moon &mn, double xval, double yval,
+                          double blend)
 {
-  const double thetax = asin (xval) * 180 / PI;
-  const double thetay = acos (yval) * 180 / PI;
-
-  const double offset = (perlin2DPoint (1.0, mn.smoothness,
-                                        mn.xoffset + mn.x + thetax,
-                                        mn.yoffset + mn.y + thetay,
-                                        mn.lambda, mn.octaves) + 1) / 2;
-  const double percentVal = (perlin2DPoint (1.0, mn.smoothness,
-                             mn.xoffset + mn.x * 1000 + thetax,
-                             mn.yoffset + mn.y * 1000 + thetay,
-                             mn.lambda, mn.octaves) + 1) / 2;
-
-  set_add_blender (0, 0, 0, (int)(blend * xval * percentVal * offset * 255));
-#ifdef THREADS
-  drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-#endif
-  putpixel (bmp, x, y, mn.col1);
-  set_add_blender (0, 0, 0, (int)(blend * xval * (1 - percentVal) * offset * 255));
-  putpixel (bmp, x, y, mn.col2);
-#ifdef THREADS
-  solid_mode();
-#endif
+	const double thetax  = RAD2DEG(asin(xval));
+	const double thetay  = RAD2DEG(acos(yval));
+	const double offset  = (perlin2DPoint (1., mn.smoothness,
+	                                       mn.xoffset + mn.x + thetax,
+	                                       mn.yoffset + mn.y + thetay,
+	                                       mn.lambda, mn.octaves) + 1.) / 2.;
+	const double percVal = (perlin2DPoint (1.0, mn.smoothness,
+	                                       mn.xoffset + mn.x * 1000 + thetax,
+	                                       mn.yoffset + mn.y * 1000 + thetay,
+	                                       mn.lambda, mn.octaves) + 1) / 2;
+
+	set_add_blender (0, 0, 0, blend * xval * percVal * offset * 255);
+	drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
+	putpixel (mn.bitmap, x, y, mn.col1);
+	set_add_blender (0, 0, 0, blend * xval * (1. - percVal) * offset * 255);
+	putpixel (mn.bitmap, x, y, mn.col2);
+	drawing_mode (global.current_drawing_mode, NULL, 0, 0);
 }
 
 
-
-/*****************************************************************************
-draw_amoon
-
-Renders a single moon onto a bitmap.  Assumes transparent drawing is enabled.
-Abeys the given bounding box, which may be smaller than the moon itself.
-Uses the darkside parameter to decide which side of the moon should be
-dark.  Obeys and updates the z-buffer.
-
-The current implementation of this function is beggins for some
-simplifications.  And again, what about thoes [xy]offset variables?
-*****************************************************************************/
-static void draw_amoon( BITMAP* bmp,
-                        const moon& mn, int x0, int y0, int x1, int y1,
-                        bool darkside, ZBuffer& zbuffer )
-{
-  for ( int y = y0; y != y1; ++y )
-    {
-      bool hityet = false;
-
-      for ( int x = x0; x != x1; ++x )
-        {
-          /* Occupied? */
-          if ( zbuffer.test(x,y) )
-            continue ;
-
-          /* Find distance from this moon */
-          int xdist = mn.x - x;
-          int ydist = mn.y - y;
-
-          /* Compute some other nice circle values */
-          const double fradius = (double) mn.radius ;
-          double xval = (double)xdist / fradius;
-          double yval = (double)ydist / fradius;
-          double distance2 = (xdist * xdist) + (ydist * ydist);
-          double distance = sqrt (distance2);
-
-          /* A bound check -> are we in the circle? */
-          if ( distance > fradius + 1 )
-            {
-              if ( hityet )	/* If we've already been inside at this y... */
-                break;		/* then skip ahead to the next y */
-              else			/* Otherwise... */
-                continue ;	/* Stay at this y, and skip to the next x */
-            }
-
-          /* Edges use lighter blending */
-          const double edgeval = coverage( distance, fradius );
-
-          /* Now, should we paint this side of the moon? */
-          if ( xval && ((xval<0) == darkside) )
-            {
-              paint_moonpix(
-                bmp, x, y,
-                mn, fabs(xval), yval,
-                //fract_clamp( fabs(xval) ),
-                //fract_clamp( fabs(yval) ),
-                edgeval ) ;
-            }
-
-          /* Mark this pixel as occupied */
-          zbuffer.set(x,y);
-          hityet = true ;
-        }
-    }
-}
-
-
-
 /*****************************************************************************
 draw_moons
 
 Renders a set of moons over a given bitmap.  The bitmap to draw of and the
 appropriate dimensions must be given.
 *****************************************************************************/
-void draw_moons (BITMAP* bmp, int width, int height)
+static void draw_moons (LevelCreator* lcr, int32_t width, int32_t height)
 {
-  const bool darkside = rand() > (RAND_MAX/2+1) ;
-  ZBuffer	zbuffer( width, height ) ;
-
-  drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-  for ( int numMoons = (int)central_rand( 14.0 );
-        numMoons; --numMoons )
-    {
-      int x0, y0, x1, y1;
-
-      /* Make up a moon */
-      const moon mn = generate_moon( width, height );
-
-      /* Where is it? */
-      x0 = clamped_int( mn.x - mn.radius -1, 0, width ) ;
-      y0 = clamped_int( mn.y - mn.radius -1, 0, height ) ;
-      x1 = clamped_int( mn.x + mn.radius +1, 0, width ) ;
-      y1 = clamped_int( mn.y + mn.radius +1, 0, height ) ;
-
-      /* Draw it */
-      draw_amoon( bmp, mn, x0, y0, x1, y1, darkside, zbuffer );
-    }
-  solid_mode ();
+	const bool darkside = rand() > (RAND_MAX / 2 + 1);
+	ZBuffer	   zbuffer( width, height ) ;
+
+	for (int32_t numMoons = central_rand( 14.0 ); numMoons; --numMoons) {
+		/* Make up a moon */
+		const moon mn(width, height);
+
+		/* Where is it? */
+		int32_t x0 = clamped_int(mn.x - mn.radius, 0, width );
+		int32_t y0 = clamped_int(mn.y - mn.radius, 0, height);
+		int32_t x1 = clamped_int(mn.x + mn.radius, 0, width );
+		int32_t y1 = clamped_int(mn.y + mn.radius, 0, height);
+
+		/* Draw it */
+		draw_amoon(lcr, mn, x0, y0, x1, y1, darkside, zbuffer);
+	}
 }
 
 
@@ -355,83 +368,60 @@ generate_sky
 
 Given some input parameters, renders a sky (with moons) onto a bitmap.
 *****************************************************************************/
-void generate_sky (GLOBALDATA *global, BITMAP* bmp, const gradient* grad, int flags )
-{
-  double messiness = (rand () % 100 / 1000.0 + 0.05);
-  const int xoffset = rand( );	/* For perlin, random starting x */
-  const int yoffset = rand( );	/* For perlin, random starting y */
-
-  for (int x = 0; x < global->screenWidth; x++)
-    {
-      for (int y = 0; y < global->screenHeight - MENUHEIGHT; y++)
-        {
-          double offset = 0;
-
-          if ( flags & GENSKY_DETAILED )
-            offset += perlin2DPoint (1.0, 200, xoffset + x, yoffset + y, 0.3, 6) * ((global->screenHeight - MENUHEIGHT) * messiness);
-
-          if ( flags & GENSKY_DITHERGRAD )
-            offset += rand () % 10 - 5;
-
-          while (y + offset < 0)
-            offset /= 2;
-          while (y + offset + 1 > (global->screenHeight - MENUHEIGHT))
-            offset /= 2;
-
-          #ifdef THREADS
-          solid_mode();
-          #endif
-          putpixel (bmp, x, y,
-                    gradientColorPoint (grad, global->screenHeight - MENUHEIGHT, y + offset));
-          #ifdef THREADS
-          drawing_mode(global->env->current_drawing_mode, NULL, 0, 0);
-          #endif
-
-        }
-    }
-  draw_moons (bmp, global->screenWidth, global->screenHeight);
-}
-
-
-
-// This function should be a seperate thread which constantly generates
-// skies in the background so as to not to hang the game after the
-// buying screen.
-void *Generate_Sky_In_Background(void *new_env)
+void generate_sky(LevelCreator* lcr, const gradient* grad, int32_t flags )
 {
-   ENVIRONMENT *env = (ENVIRONMENT *) new_env;
-   GLOBALDATA *my_global = env->Get_Globaldata();
-   BITMAP *sky_in_progress = NULL;
-
-   // do this constantly
-   while ( my_global->get_command() != GLOBAL_COMMAND_QUIT )
-   {
-      // create a bitmap in waiting, if none exists
-      if (! env->get_waiting_sky())
-      {
-         sky_in_progress = create_bitmap( my_global->screenWidth, my_global->screenHeight - MENUHEIGHT);
-         generate_sky (my_global, sky_in_progress, env->my_sky_gradients[my_global->cursky],
-                   (my_global->ditherGradients ? GENSKY_DITHERGRAD : 0 ) |
-                   (my_global->detailedSky ? GENSKY_DETAILED : 0 )  );
-
-         #ifdef THREADS
-         env->lock(env->waiting_sky_lock);
-         #endif
-         env->waiting_sky = sky_in_progress;
-         #ifdef THREADS
-         env->unlock(env->waiting_sky_lock);
-         #endif
-         sky_in_progress = NULL;
-      }
-
-      LINUX_SLEEP;
-   }
-
-   #ifdef THREADS
-   pthread_exit(NULL);
-   return NULL;         // we never hit this, but it keeps the compiler from complaining
-   #else
-   return NULL;
-   #endif
+	double messiness  = (static_cast<double>(rand () % 100) / 1000.0 + 0.05);
+	const int xoffset = rand( );  // For perlin, random starting x
+	const int yoffset = rand( );  // For perlin, random starting y
+
+	temp_sky = create_bitmap( env.sky->w, env.sky->h );
+	clear_to_color(temp_sky, BLACK);
+	clear_to_color(env.sky, BLACK);
+
+	for (int32_t x = 0
+	     ; (!lcr || lcr->can_work()) && (x < env.screenWidth)
+	     ; ++x) {
+		for (int32_t y = 0
+		     ; (!lcr || lcr->can_work()) && (y < (env.screenHeight - MENUHEIGHT))
+		     ; ++y) {
+
+			lcr->yield();
+
+			double offset = 0;
+
+			if ( flags & GENSKY_DETAILED )
+				offset += perlin2DPoint(1., 200, xoffset + x, yoffset + y, .3, 6)
+				        * (static_cast<double>(env.screenHeight - MENUHEIGHT)
+				           * messiness);
+
+			if ( flags & GENSKY_DITHERGRAD )
+				offset += (rand () % 10) - 5;
+
+			while ( ( (y + offset) < 0)
+			     || ( (y + offset + 1) > (env.screenHeight - MENUHEIGHT) ) )
+				offset /= 2;
+
+			global.lockLand();
+			solid_mode();
+			putpixel (temp_sky, x, y,
+			          gradientColorPoint(grad, env.screenHeight - MENUHEIGHT,
+			                             y + offset));
+			drawing_mode(global.current_drawing_mode, NULL, 0, 0);
+			global.unlockLand();
+		}
+	}
+	draw_moons (lcr, env.screenWidth, env.screenHeight - MENUHEIGHT);
+
+	// Put temp sky onto the real bitmap:
+	global.lockLand();
+	solid_mode();
+	blit(temp_sky, env.sky, 0, 0, 0, 0,
+	     env.sky->w, env.sky->h);
+	global.unlockLand();
+
+	// clean up
+	if (temp_sky)
+		destroy_bitmap(temp_sky);
+	temp_sky = nullptr;
 }
 
diff --git a/src/sky.h b/src/sky.h
index 53d0280..c432616 100644
--- a/src/sky.h
+++ b/src/sky.h
@@ -1,189 +1,185 @@
 #ifndef SKY_HEADER_FILE__
 #define SKY_HEADER_FILE__
 
-#ifdef THREADS
-#include <pthread.h>
-#endif
-
-
+#include "main.h"
 
+#define SKIES 8
+// The first SKIES are regular, the second are crispy.
 
 // Sky gradients, first line is top of screen
 const gradient sky_gradient1[] =
 {
-  {{255,255,255,0}, 0.0},
-  {{100,100,100,0}, 0.1},
-  {{ 80,100,100,0}, 0.5},
-  {{0,0,0,0}, -1}
+	{{255,255,255,0}, 0.0},
+	{{100,100,100,0}, 0.1},
+	{{ 80,100,100,0}, 0.5},
+	{{0,0,0,0}, -1}
 };
 
 const gradient sky_gradient2[] =
 {
-  {{  0,  0, 40,0}, 0.0},
-  {{ 40, 40,100,0}, 0.5},
-  {{ 80,80,100,0}, 0.75},
-  {{0,0,0,0}, -1}
+	{{  0,  0, 40,0}, 0.0},
+	{{ 40, 40,100,0}, 0.5},
+	{{ 80,80,100,0}, 0.75},
+	{{0,0,0,0}, -1}
 };
 
 const gradient sky_gradient3[] =
 {
-  {{240,  0, 40,0}, 0.0},
-  {{140, 40,100,0}, 0.15},
-  {{ 80, 80,100,0}, 0.75},
-  {{0,0,0,0}, -1}
+	{{240,  0, 40,0}, 0.0},
+	{{140, 40,100,0}, 0.15},
+	{{ 80, 80,100,0}, 0.75},
+	{{0,0,0,0}, -1}
 };
 
 const gradient sky_gradient4[] =
 {
-  {{ 40, 40, 40,0}, 0.0},
-  {{100, 40,100,0}, 0.2},
-  {{ 80, 80,100,0}, 0.75},
-  {{0,0,0,0}, -1}
+	{{ 40, 40, 40,0}, 0.0},
+	{{100, 40,100,0}, 0.2},
+	{{ 80, 80,100,0}, 0.75},
+	{{0,0,0,0}, -1}
 };
 
 const gradient sky_gradient5[] =
 {
-  {{  0, 90, 40,0}, 0.0},
-  {{  0,120,100,0}, 0.2},
-  {{ 40,200,100,0}, 0.75},
-  {{0,0,0,0}, -1}
+	{{  0, 90, 40,0}, 0.0},
+	{{  0,120,100,0}, 0.2},
+	{{ 40,200,100,0}, 0.75},
+	{{0,0,0,0}, -1}
 };
 
 // Sunset
 const gradient sky_gradient6[] =
 {
-  {{ 70,240,240,0}, 0.0},
-  {{ 70,200,200,0}, 0.3},
-  {{ 70,200,160,0}, 0.35},
-  {{255,200, 70,0}, 0.6},
-  {{255,255,128,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 70,240,240,0}, 0.0},
+	{{ 70,200,200,0}, 0.3},
+	{{ 70,200,160,0}, 0.35},
+	{{255,200, 70,0}, 0.6},
+	{{255,255,128,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Burning sky
 const gradient sky_gradient7[] =
 {
-  {{ 20, 20, 20,0}, 0.0},
-  {{255,200,  0,0}, 0.08},
-  {{255,255,  0,0}, 0.13},
-  {{ 20, 20, 20,0}, 0.16},
-  {{255,200,  0,0}, 0.5},
-  {{255,255,  0,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 20, 20, 20,0}, 0.0},
+	{{255,200,  0,0}, 0.08},
+	{{255,255,  0,0}, 0.13},
+	{{ 20, 20, 20,0}, 0.16},
+	{{255,200,  0,0}, 0.5},
+	{{255,255,  0,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Burning landscape, black skies
 const gradient sky_gradient8[] =
 {
-  {{  0,  0,  0,0}, 0.0},
-  {{100,  0,  0,0}, 0.4},
-  {{255,255,255,0}, 0.8},
-  {{0,0,0,0}, -1}
+	{{  0,  0,  0,0}, 0.0},
+	{{100,  0,  0,0}, 0.4},
+	{{255,255,255,0}, 0.8},
+	{{0,0,0,0}, -1}
 };
 
 // Sky gradients, first line is top of screen
 // dark blue to darker blue
 const gradient sky_gradient9[] =
 {
-  {{ 90, 90,255,0}, 0.0},
-  {{ 60, 60,200,0}, 0.3},
-  {{ 30, 30,150,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{ 90, 90,255,0}, 0.0},
+	{{ 60, 60,200,0}, 0.3},
+	{{ 30, 30,150,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // dark blue to blue-grey
 const gradient sky_gradient10[] =
 {
-  {{110,110,255,0}, 0.0},
-  {{150,150,255,0}, 0.3},
-  {{200,200,255,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{110,110,255,0}, 0.0},
+	{{150,150,255,0}, 0.3},
+	{{200,200,255,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // white to grey-blue to dark blue
 const gradient sky_gradient11[] =
 {
-  {{255,255,255,0}, 0.0},
-  {{200,200,255,0}, 0.3},
-  {{ 80, 80,180,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{255,255,255,0}, 0.0},
+	{{200,200,255,0}, 0.3},
+	{{ 80, 80,180,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // simple purple: dark to light
 const gradient sky_gradient12[] =
 {
-  {{133, 33,133,0}, 0.0},
-  {{220,120,220,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{133, 33,133,0}, 0.0},
+	{{220,120,220,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // night sky: black to dark purple
 const gradient sky_gradient13[] =
 {
-  {{  0,  0,  0,0}, 0.0},
-  {{ 50,  0, 50,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{  0,  0,  0,0}, 0.0},
+	{{ 50,  0, 50,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Sunset
 const gradient sky_gradient14[] =
 {
-  {{  0,  0,  0,0}, 0.0},
-  {{ 50,  0, 50,0}, 0.1},
-  {{ 90, 20,  0,0}, 0.3},
-  {{180, 50,  0,0}, 0.7},
-  {{255,150,150,0}, 0.9},
-  {{255,255,100,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{  0,  0,  0,0}, 0.0},
+	{{ 50,  0, 50,0}, 0.1},
+	{{ 90, 20,  0,0}, 0.3},
+	{{180, 50,  0,0}, 0.7},
+	{{255,150,150,0}, 0.9},
+	{{255,255,100,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Burning sky
 const gradient sky_gradient15[] =
 {
-  {{185, 60, 60,0}, 0.0},
-  {{240,110,110,0}, 0.5},
-  {{255,110,110,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{185, 60, 60,0}, 0.0},
+	{{240,110,110,0}, 0.5},
+	{{255,110,110,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 // Burning landscape, black skies
 const gradient sky_gradient16[] =
 {
-  {{  0,  0,  0,0}, 0.0},
-  {{170, 50, 50,0}, 0.5},
-  {{220,110,110,0}, 1.0},
-  {{0,0,0,0}, -1}
+	{{  0,  0,  0,0}, 0.0},
+	{{170, 50, 50,0}, 0.5},
+	{{220,110,110,0}, 1.0},
+	{{0,0,0,0}, -1}
 };
 
 
 const gradient * const sky_gradients[] =
 {
-  sky_gradient1,
-  sky_gradient2,
-  sky_gradient3,
-  sky_gradient4,
-  sky_gradient5,
-  sky_gradient6,
-  sky_gradient7,
-  sky_gradient8,
-  sky_gradient9,
-  sky_gradient10,
-  sky_gradient11,
-  sky_gradient12,
-  sky_gradient13,
-  sky_gradient14,
-  sky_gradient15,
-  sky_gradient16
+	sky_gradient1,
+	sky_gradient2,
+	sky_gradient3,
+	sky_gradient4,
+	sky_gradient5,
+	sky_gradient6,
+	sky_gradient7,
+	sky_gradient8,
+	sky_gradient9,
+	sky_gradient10,
+	sky_gradient11,
+	sky_gradient12,
+	sky_gradient13,
+	sky_gradient14,
+	sky_gradient15,
+	sky_gradient16
 };
 
 
+#define GENSKY_DETAILED		1
+#define GENSKY_DITHERGRAD	2
 
-void draw_moons (BITMAP* bmp, int width, int height);
-
-void *Generate_Sky_In_Background(void *new_env);
-
+class LevelCreator;
 
+void generate_sky(LevelCreator* lcr, const gradient* grad, int32_t flags);
 
 #endif
-
-
diff --git a/src/sound.cpp b/src/sound.cpp
new file mode 100644
index 0000000..e039d33
--- /dev/null
+++ b/src/sound.cpp
@@ -0,0 +1,174 @@
+#include "sound.h"
+
+// max volume factor: means that the interval 0% -> 100% is split in 5
+int32_t MAX_VOLUME_FACTOR = 5;
+
+// General helper that unifies the playing
+static void play_sound (eSounds sound, int32_t x, int32_t vol, int32_t freq);
+
+
+/** @brief play a weapon or item fire sample according to @a type, panned using @a x.
+  *
+  * This just plays what weapon[]/item[] globals hold for a sound.
+  *
+  * @param[in] type The weapon or item type.
+  * @param[in] x The x-coordinate where the sound is played.
+  * @param[in] vol The volume (0 - 255)
+  * @param[in] freq Frequency, 1000 is normal, 500 is half, 2000 is double and so on.
+**/
+void play_fire_sound(int32_t type, int32_t x, int32_t vol, int32_t freq)
+{
+	int32_t sndNum = -1;
+
+	if (type >= WEAPONS) {
+		if (item[type - WEAPONS].sound > -1)
+			sndNum = item[type - WEAPONS].sound;
+	} else {
+		if (weapon[type].sound > -1)
+			sndNum = weapon[type].sound;
+	}
+
+	if ( (sndNum > -1) && (sndNum < SND_COUNT) )
+		play_sound(static_cast<eSounds>(sndNum), x, vol, freq);
+}
+
+
+/** @brief play a weapon or item explosion sample according to @a type, panned using @a x.
+  *
+  * This plays the sound listed in weapon[]/item[] globals unless special rules
+  * apply.
+  *
+  * @param[in] type The weapon or item type.
+  * @param[in] x The x-coordinate where the sound is played.
+  * @param[in] vol The volume (0 - 255)
+  * @param[in] freq Frequency, 1000 is normal, 500 is half, 2000 is double and so on.
+**/
+void play_explosion_sound(int32_t type, int32_t x, int32_t vol, int32_t freq)
+{
+	int32_t sndNum = -1;
+
+	if (SHAPED_CHARGE == type)
+		sndNum = SND_EXPL_SHAPED_CHARGE;
+	else if (WIDE_BOY == type)
+		sndNum = SND_EXPL_WIDE_BOY;
+	else if (CUTTER == type)
+		sndNum = SND_EXPL_CUTTER;
+	else if ( (SML_NAPALM <= type) && (LRG_NAPALM >= type)) {
+		sndNum = SND_EXPL_NAPALM;
+		freq += 333 * (LRG_NAPALM - type);
+	} else if (NAPALM_JELLY == type) {
+		sndNum = SND_EXPL_NAPALM_BURN;
+		freq  += (rand() % 200) - 100;
+		vol   -= rand() % 64;
+	} else if (PERCENT_BOMB == type)
+		sndNum = SND_EXPL_PER_CENT_BOMB;
+	else if (REDUCER == type)
+		sndNum = SND_EXPL_REDUCER;
+	else if ( ((DIRT_BALL   <= type) && (SMALL_DIRT_SPREAD >= type))
+	       || ((RIOT_CHARGE <= type) && (RIOT_BLAST        >= type)) )
+		sndNum = SND_EXPL_DIRT_BALL_BOMB;
+	else {
+		// Default handling
+		if (type >= WEAPONS)
+			sndNum = item[type - WEAPONS].sound + SND_EXPL_MISS_SML;
+		else
+			sndNum = weapon[type].sound + SND_EXPL_MISS_SML;
+	}
+
+	if ( (sndNum > -1) && (sndNum < SND_COUNT) )
+		play_sound(static_cast<eSounds>(sndNum), x, vol, freq);
+}
+
+
+/** @brief plays the currently set background music modified by set volume factor
+**/
+void play_music()
+{
+    if (env.loadBackgroundMusic())
+       play_sound(SND_BG_MUSIC, 128, 255, 1000);
+}
+
+
+/** @brief play a natural sample according to @a type, panned using @a x.
+  *
+  * @param[in] type The natural type.
+  * @param[in] x The x-coordinate where the sound is played.
+  * @param[in] vol The volume (0 - 255)
+  * @param[in] freq Frequency, 1000 is normal, 500 is half, 2000 is double and so on.
+**/
+
+void play_natural_sound(int32_t type, int32_t x, int32_t vol, int32_t freq)
+{
+	int32_t sndNum = -1;
+
+	if (SML_METEOR == type) {
+		sndNum = naturals[SML_METEOR - WEAPONS].sound;
+		vol   -= rand() % 128;
+		freq  += 100 + (rand() % 100);
+	} else if (MED_METEOR == type) {
+		sndNum = naturals[MED_METEOR - WEAPONS].sound;
+		vol   -= rand() % 64;
+		freq  += rand() % 100;
+	} else if (LRG_METEOR == type) {
+		sndNum = naturals[LRG_METEOR - WEAPONS].sound;
+		vol   -= rand() % 64;
+		freq  += rand() % 250;
+	} else if (SML_LIGHTNING == type) {
+		sndNum = naturals[SML_LIGHTNING - WEAPONS].sound;
+		vol   -= rand() % 128;
+		freq  += 100 + (rand() % 100);
+	} else if (MED_LIGHTNING == type) {
+		sndNum = naturals[MED_LIGHTNING - WEAPONS].sound;
+		vol   -= rand() % 64;
+		freq  += rand() % 100;
+	} else if (LRG_LIGHTNING == type) {
+		sndNum = naturals[LRG_LIGHTNING - WEAPONS].sound;
+		vol   -= rand() % 64;
+		freq  += rand() % 250;
+	} else if (DIRT_FRAGMENT == type) {
+		sndNum = SND_NATU_DIRT_FALL;
+		vol   -= rand() % 64;
+		// freq is manipulated by the call
+	}
+
+	if ( (sndNum > -1) && (sndNum < SND_COUNT)
+	  && ( (SND_NATU_DIRT_FALL != sndNum)
+		|| (global.used_voices < (env.voices - 8))) )
+		play_sound(static_cast<eSounds>(sndNum), x, vol, freq);
+}
+
+
+/** @brief play an interface sample according to @a sound.
+  *
+  * @param[in] sound The sound to play.
+**/
+
+void play_interface_sound(eSounds sound)
+{
+	if (SND_INTE_BUTTON_CLICK == sound)
+		play_sound(sound, env.halfWidth, 128, 1000);
+}
+
+
+// Global helpers implementation
+static void play_sound(eSounds sound, int32_t x, int32_t vol, int32_t freq)
+{
+	if (global.used_voices < env.voices) {
+		int32_t xVol = (vol * env.volume_factor) / MAX_VOLUME_FACTOR;
+
+		if ( (sound < SND_BG_MUSIC)
+		  && env.sounds[sound]
+		  && (vol  > 0)
+		  && (freq > 0) ) {
+			play_sample(env.sounds[sound],
+			            xVol,
+			            x <= 0 ? 31
+			            : x >= env.screenWidth ? 192
+			            : 31 + (x * 191 / env.screenWidth),
+			            freq, false);
+		} else if (SND_BG_MUSIC == sound)
+			play_sample(env.background_music, xVol, x, freq, true);
+
+		++global.used_voices;
+	}
+}
diff --git a/src/imagedefs.h b/src/sound.h
similarity index 58%
rename from src/imagedefs.h
rename to src/sound.h
index f09fa5c..7dc3d00 100644
--- a/src/imagedefs.h
+++ b/src/sound.h
@@ -1,9 +1,9 @@
-#ifndef IMAGEDEFS_DEFINE
-#define IMAGEDEFS_DEFINE
+#pragma once
+#ifndef ATANKS_SRC_SOUNDS_H_INCLUDED
+#define ATANKS_SRC_SOUNDS_H_INCLUDED
 
 /*
  * atanks - obliterate each other with oversize weapons
- * Copyright (C) 2003  Thomas Hudson
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -12,18 +12,26 @@
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+ * 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 
+ * 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.
- * */
+ *
+ */
+
+#include "externs.h"
+
+/** @file sound.h
+  * @brief declare centralized functions to play sounds.
+**/
+
+void play_explosion_sound(int32_t type,  int32_t x, int32_t vol, int32_t f_off);
+void play_fire_sound     (int32_t type,  int32_t x, int32_t vol, int32_t f_off);
+void play_music          ();
+void play_natural_sound  (int32_t type,  int32_t x, int32_t vol, int32_t f_off);
+void play_interface_sound(eSounds sound);
 
-#define	DONE_IMAGE		11
-#define	FAST_UP_ARROW_IMAGE	12
-#define	UP_ARROW_IMAGE		13
-#define	DOWN_ARROW_IMAGE	14
-#define	FAST_DOWN_ARROW_IMAGE	15
+#endif // ATANKS_SRC_SOUNDS_H_INCLUDED
 
-#endif // IMAGEDEFS_DEFINE
diff --git a/src/tank.cpp b/src/tank.cpp
index 28b10f6..1f29149 100644
--- a/src/tank.cpp
+++ b/src/tank.cpp
@@ -18,8 +18,6 @@
  * */
 
 
-#include "environment.h"
-#include "globaldata.h"
 #include "floattext.h"
 #include "explosion.h"
 #include "teleport.h"
@@ -27,1409 +25,1706 @@
 #include "player.h"
 #include "beam.h"
 #include "tank.h"
+#include "sound.h"
 
+#include <cassert> // For some checks if _DEBUG is defined.
+#include <mutex>   // Needed for lock_guard
 
-/*
-The deconstructor should be split into two pieces. One function for
-destroying a tank and another strictly for cleaning up a tank's memory
-whether it was destoryed or not.
--- Jesse
-*/
-TANK::~TANK ()
+TANK::TANK () :
+	PHYSICAL_OBJECT(false),
+	healthText(nullptr, -1, -1, 0., 0., WHITE,     CENTRE, TS_NO_SWAY, -1),
+	nameText(  nullptr, -1, -1, 0., 0., WHITE,     CENTRE, TS_NO_SWAY, -1),
+	shieldText(nullptr, -1, -1, 0., 0., TURQUOISE, CENTRE, TS_NO_SWAY, -1)
 {
-/*
-  #ifdef NETWORK
-  if (player)        // we should always have a player, but better safe than sorry
-  {
-      int player_index = 0;
-      bool found = false;
-      char buffer[64];
-      while ( (player_index < _global->numPlayers) && (!found) )
-      {
-          // get the player index
-          if ( ( _global->players[player_index]->tank ) && (_global->players[player_index]->tank == this) )
-              found = true;
-          else
-              player_index++;
-     }
-     if (found)        // we should have found a match and now we send it to all clients
-     {
-         sprintf(buffer, "REMOVETANK %d", player_index);
-         _global->Send_To_Clients(buffer);
-     }
-  }
-  #endif
-  if (_global && player)
-    {
-      int random_item;
-      if (_global->violent_death)
-        {
-          random_item = rand() % VIOLENT_CHANCE;
-          if ( ( random_item > _global->violent_death ) && (random_item <= VIOLENT_DEATH_HEAVY) )
-            random_item = (int) _global->violent_death;
-
-          switch (random_item)
-            {
-            case VIOLENT_DEATH_LIGHT:
-              player->ni[ITEM_VENGEANCE] += 1;
-              break;
-            case VIOLENT_DEATH_MEDIUM:
-              player->ni[ITEM_DYING_WRATH] += 1;
-              break;
-            case VIOLENT_DEATH_HEAVY:
-              player->ni[ITEM_FATAL_FURY] += 1;
-              break;
-            }     //end of switch
-        }
-
-      if (player->ni[ITEM_FATAL_FURY] > 0)
-        {
-          int numLaunch = (int)item[ITEM_FATAL_FURY].vals[SELFD_NUMBER];
-          cw = (int)item[ITEM_FATAL_FURY].vals[SELFD_TYPE];
-          player->ni[ITEM_FATAL_FURY]--;
-          player->nm[cw] += numLaunch;
-          for (int count = numLaunch; count > 0; count--)
-            {
-              a = rand () % 180 + 90;
-              p = rand () % (MAX_POWER / 2);
-              activateCurrentSelection ();
-            }
-        }
-      else if (player->ni[ITEM_DYING_WRATH] > 0)
-        {
-          int numLaunch = (int)item[ITEM_DYING_WRATH].vals[SELFD_NUMBER];
-          cw = (int)item[ITEM_DYING_WRATH].vals[SELFD_TYPE];
-          player->ni[ITEM_DYING_WRATH]--;
-          player->nm[cw] += numLaunch;
-          for (int count = numLaunch; count > 0; count--)
-            {
-              a = rand () % 180 + 90;
-              p = rand () % (MAX_POWER / 2);
-              activateCurrentSelection ();
-            }
-        }
-      else if (player->ni[ITEM_VENGEANCE] > 0)
-        {
-          int numLaunch = (int)item[ITEM_VENGEANCE].vals[SELFD_NUMBER];
-          cw = (int)item[ITEM_VENGEANCE].vals[SELFD_TYPE];
-          player->ni[ITEM_VENGEANCE]--;
-          player->nm[cw] += numLaunch;
-          for (int count = numLaunch; count > 0; count--)
-            {
-              a = rand () % 180 + 90;
-              p = rand () % (MAX_POWER / 2);
-              activateCurrentSelection ();
-            }
-        }
-    }
-   */
+	// The shield phase delta depends on currently set FPS
+	shld_delta /= static_cast<double>(env.frames_per_second);
 
-  if (player)
-    {
-      player->tank = NULL;
-      player = NULL;
-    }
-  if (_global)
-    {
-      _global->numTanks--;
-      if (_global->currTank == this)
-        _global->currTank = NULL;
-    }
+	setTextPositions(false);
+
+	drag  = 0.5;
+	mass  = 3000;
+	a    += rand () % 180;
 
-  if (shieldText)
-    delete (shieldText);
-  if (healthText)
-    delete (healthText);
-  if (nameText)
-    delete (nameText);
-
-  if (_env)
-    _env->removeObject(this);
-
-  shieldText  = NULL;
-  healthText  = NULL;
-  nameText    = NULL;
-  _env        = NULL;
-  _global     = NULL;
-  creditTo    = NULL;
-  _target     = NULL;
+	// Add to the chain:
+	global.addObject(this);
+	global.numTanks++;
 }
 
-TANK::TANK (GLOBALDATA *global, ENVIRONMENT *env):PHYSICAL_OBJECT(),_target(NULL),creditTo(NULL),
-                                                healthText(NULL),shieldText(NULL),nameText(NULL)
-{
-  setEnvironment (env);
-  _global = global;
-  // Ask for memory
-  healthText = new FLOATTEXT (global, env, NULL, 0, 0, WHITE, CENTRE);
-  if (!healthText)
-    {
-      perror ( "tank.cc: Failed allocating memory for healthText in TANK::TANK");
-      // exit (1);
-    }
 
-  shieldText = new FLOATTEXT (global, env, NULL, 0, 0, makecol (200, 200, 255), CENTRE);
-  if (!shieldText)
-    {
-      perror ( "tank.cc: Failed allocating memory for shieldText in TANK::TANK");
-      // exit (1);
-    }
+/*
+The destructor only removes the tank and cleans up after it.
+Any tank destruction (with big badass explosions, vengeance and stuff) is done
+in TANK::explode().
+*/
+TANK::~TANK ()
+{
+	if (player) {
+		player->tank = nullptr;
+		player       = nullptr;
+	}
+	creditTo = nullptr;
+
+	global.numTanks--;
+	if (global.get_curr_tank() == this)
+		global.set_curr_tank(nullptr);
+
+	// Take out of the chain:
+	global.removeObject(this);
+}
 
-  if (global->name_above_tank)
-    {
-      nameText = new FLOATTEXT (global, env, NULL, 0, 0, WHITE, CENTRE);
-      if (! nameText)
-        {
-          perror ( "tank.cc: Failed allocating memory for nameText in TANK::TANK");
-          // exit(1);
-        }
-    }
 
-  // Other initial pointers:
-  _align = LEFT;
-  _global->numTanks++;
+/// @brief Set texts to vertical bounce and initialize text positions
+void TANK::activate()
+{
+	shieldText.set_sway_type(TS_VERTICAL);
+	healthText.set_sway_type(TS_VERTICAL);
+	nameText.set_sway_type(TS_VERTICAL);
 
-  player = NULL;
+	setTextPositions(true);
 }
 
-void TANK::initialise ()
-{
-  PHYSICAL_OBJECT::initialise ();
-  drag = 0.5;
-  mass = 3000;
-  repulsion = 0;
-  shieldColor = 0;
-  shieldThickness = 0;
-  t = 0;
-  sh = 0;
-  _targetX = -1;
-  _targetY = -1;
-  newRound ();
-}
 
-void TANK::newRound ()
+/// @brief activate (aka "fire" or "shoot") whatever is selected right now.
+/// Use this whenever a weapon is fired without the player hitting the trigger.
+/// For the trigger slamming use simActivateCurrentSelection().
+void TANK::activateCurrentSelection()
 {
-  char buf[10];
-
-  cw = 0;
-  damage = 0;
-  pen = 0;
-  para = 0;
-  creditTo = NULL;
-  p = MAX_POWER / 2;
-  a = (rand () % 180) + 90;
-  if (sh > 0 && sht > 0)
-    if (player)
-      player->ni[sht]++;
-  sh = 0;
-  repulsion = 0;
-  // shPhase = rand () % 360;
-  shPhase = 0;
-  delta_phase = 0.1;
-  sht = 0;
-  l = 100;
-  repair_rate = 0;
-  if (player)
-    {
-      double tmpL = 0;
-      tmpL += (int)(player->ni[ITEM_ARMOUR] * item[ITEM_ARMOUR].vals[0]);
-      tmpL += (int)(player->ni[ITEM_PLASTEEL] * item[ITEM_PLASTEEL].vals[0]);
-      repair_rate = Get_Repair_Rate();
-      if (tmpL > 0)
-        l += (int)pow (tmpL, 0.6);
-
-      if (healthText)
-        healthText->set_color( player->color );
-      if (nameText)
-        {
-          nameText->set_text ( player->getName() );
-          nameText->set_color ( player->color );
-        }
-    }
-  maxLife = l;
-  ds = 0;
-  fs = 0;
-  sprintf (buf, "%d", l);
-  healthText->set_text (buf);
-  // delay_fall = GRAVITY_DELAY;
-  delay_fall = (int) labs(_env->landSlideDelay * 100);
-  fire_another_shot = 0;
-  shots_fired = 0;
-
+	// avoid firing weapons on exit in Windows
+	if ( (global.get_command() == GLOBAL_COMMAND_QUIT)
+	  || (global.get_command() == GLOBAL_COMMAND_MENU) )
+		return;
+
+	// This must not be called outside fire stage.
+	assert( (STAGE_FIRE == global.stage)
+			&& "ERROR: TANK::activateCurrentSelection() called outside STAGE_FIRE!");
+	if (STAGE_FIRE != global.stage)
+		return;
+
+	// remove status from top bar at next redraw
+	if (global.tank_status)
+		global.tank_status[0] = 0;
+
+	// reduce time to fall, but reset if already done
+	if (--env.time_to_fall < 0)
+		env.time_to_fall = (rand() % env.landSlideDelay) + 1;
+
+	/** ==============================
+	  * === Case 1 : Fire a weapon ===
+	  * ==============================
+	**/
+	if (cw < WEAPONS) {
+		player->changed_weapon = false;
+		if (cw)
+			player->nm[cw]--;
+
+		// --- Ballistics ---
+		//--------------------
+		if (cw < BALLISTICS) {
+			play_fire_sound(cw, x, 255, 1000);
+
+			// Loop over spread, that covers everything
+			for (int32_t z = 0; z < weapon[cw].spread; ++z) {
+				int32_t ca = a + ((SPREAD * z) - (SPREAD * (weapon[cw].spread - 1) / 2));
+				double  dp = static_cast<double>(p); // Just a shortcut against further casts
+
+				// power must not be zero if this is a riot charge/blast.
+				// If it were, the calculation of the triangle goes nuts and
+				// sends the charge downwards, regardless of the set angle.
+				/// @todo : Fix the calculation so this extra check becomes obsolete
+				if ( (dp < 100.) && (RIOT_CHARGE <= cw) && (RIOT_BLAST >= cw) )
+					dp = 100.;
+
+				double mxv = env.slope[ca][0] * dp * env.FPS_mod / 100.;
+				double myv = env.slope[ca][1] * dp * env.FPS_mod / 100.;
+
+				try {
+					MISSILE* newmis = new MISSILE(player,
+					                   x + (env.slope[ca][0] * turr_off_x),
+									   y + (env.slope[ca][1] * turr_off_x),
+									   mxv, myv, cw, MT_WEAPON, 1);
+
+					// set up / check volley
+					if (weapon[cw].delay && (0 == fire_another_shot) )
+						fire_another_shot = weapon[cw].delay * env.volley_delay;
+
+					// Adapt missile drag if the player has dimpled/slick projectiles
+					if (player->ni[ITEM_DIMPLEP]) {
+						player->ni[ITEM_DIMPLEP]--;
+						newmis->drag *= item[ITEM_DIMPLEP].vals[0];
+					} else if (player->ni[ITEM_SLICKP]) {
+						player->ni[ITEM_SLICKP]--;
+						newmis->drag *= item[ITEM_SLICKP].vals[0];
+					}
+				} catch (...)  {
+					perror ( "tank.cpp: Failed to allocate memory for new"
+							 " missile in TANK::activateCurrentSelection()");
+				}
+
+			}
+		} // End of ballistics
+
+		// --- Beam weapons ---
+		//----------------------
+		else {
+			try {
+				new BEAM (player, x + (env.slope[a][0] * turr_off_x),
+                                  y + (env.slope[a][1] * turr_off_x),
+                                  a, cw, BT_WEAPON);
+			} catch (...) {
+                  perror ( "tank.cpp: Failed to allocate memory for new"
+						   " beam in TANK::activateCurrentSelection()");
+			}
+		}
+    } // End of weapons
+
+	/** =================================
+	  * === Case 2 : Activate an item ===
+	  * =================================
+	**/
+	else {
+		int32_t ci = cw - WEAPONS; // [c]urrent [i]tem
+
+		// If this is not a vengeance item, take it out of the inventory.
+		// Note: Vengeance items are reduced in explode() if triggered.
+		if ( (ITEM_VENGEANCE > ci) || (ITEM_FATAL_FURY < ci) )
+			player->ni[ci]--;
+
+		// --- Teleport ---
+		//-----------------
+		if (ITEM_TELEPORT == ci) {
+			int32_t right  = env.screenWidth  - (tank_dia * 2);
+			int32_t bottom = env.screenHeight - (tank_dia * 2) - MENUHEIGHT;
+			int32_t new_x  = (rand() % right ) + tank_dia;
+			int32_t new_y  = (rand() % bottom) + tank_dia + MENUHEIGHT;
+
+			// Be sure the tank does not end up too high in the sky
+			// or too deeply buried.
+			int32_t surf_y = global.surface[new_x].load(ATOMIC_READ);
+			if (new_y < (surf_y - 150) )
+				new_y = surf_y - 150;
+			else if (new_y > (surf_y + 100) )
+				new_y = surf_y + 100;
+
+			try {
+				new TELEPORT (this, new_x, new_y, tank_dia, 120, ci);
+				addDamage(player, 0.); // Fall is a self hit.
+				isTeleported = true;
+			} catch(...) {
+				perror ( "tank.cpp: Failed to allocate memory for teleport"
+						 " in TANK::activateCurrentSelection()");
+			}
+		}
+
+		// --- Swapper ---
+		//-----------------
+		else if (ITEM_SWAPPER == ci) {
+			TANK* other = nullptr;
+
+			while (!other) {
+				global.getHeadOfClass(CLASS_TANK, &other);
+
+				// If there are only two tanks, just take the other
+				if (2 == global.numTanks) {
+					if (other == this)
+						other->getNext(&other);
+				} else {
+					// Otherwise select one by random
+					int32_t rtn = rand() % (global.numTanks - 1);
+					while (rtn--)
+						other->getNext(&other);
+
+					// If the selection ended up with this tank, chose the next one
+					if (other == this)
+						other->getNext(&other);
+				}
+
+				assert(other && "ERROR: Swapper selected nullptr!");
+				assert( (other != this) && "ERROR: Swapper selected this as other!");
+
+				if (other == this)
+					other = nullptr;
+			} // End of selecting other
+
+			this->addDamage( player, 0.); // Own falling damage
+			this->isTeleported = true;
+			other->addDamage(player, 0.); // Their falling damage
+			other->isTeleported = true;
+
+			try {
+				// create a teleport object for this tank
+				new TELEPORT (this, other->x, other->y, tank_dia, 120, ci);
+				// create a teleport object for the other tank
+				new TELEPORT (other, x, y, other->tank_dia, 120, ci);
+			} catch (...) {
+				perror ( "tank.cpp: Failed to allocate memory for swap teleports"
+						 " in TANK::activateCurrentSelection()");
+			}
+		}
+
+		// --- Mass Teleport ---
+		//-----------------------
+		else if (ITEM_MASS_TELEPORT == ci) {
+			int32_t right  = env.screenWidth  - (tank_dia * 2);
+			int32_t bottom = env.screenHeight - (tank_dia * 2) - MENUHEIGHT;
+			TANK*   lt     = nullptr;
+
+			global.getHeadOfClass(CLASS_TANK, &lt);
+			while ( lt ) {
+				int32_t new_x  = (rand() % right ) + tank_dia;
+				int32_t new_y  = (rand() % bottom) + tank_dia + MENUHEIGHT;
+
+				// Like with the normal teleport, ensure a sane y value.
+				int32_t surf_y = global.surface[new_x].load(ATOMIC_READ);
+				if (new_y < (surf_y - 150) )
+					new_y = surf_y - 150;
+				else if (new_y > (surf_y + 100) )
+					new_y = surf_y + 100;
+				try {
+					new TELEPORT (lt, new_x, new_y, lt->tank_dia, 120, ci);
+					lt->addDamage(player, 0.); // They fall, we earn. Cool.
+					lt->isTeleported = true;
+
+				} catch(...) {
+					perror ( "tank.cpp: Failed to allocate memory for teleport"
+							 " in TANK::activateCurrentSelection()");
+				}
+				lt->getNext(&lt);
+			}
+		}
+
+		// --- Rocket ("I beliiiieve Ay Can Flaaaaaaaayy") ---
+		//-----------------------------------------------------
+		else if (ITEM_ROCKET == ci) {
+			yv = -10;
+			y -=  10;
+			if (a < 180)
+				xv += 0.3;
+			else if (a > 180)
+				xv -= 0.3;
+			// If this leads to falling damage, make sure it is a self hit:
+			addDamage(player, 0.);
+			isTeleported = true;
+			applyPhysics();
+		}
+
+		// --- Fan (aka "The most useless item there is") ---
+		//----------------------------------------------------
+		else if (ITEM_FAN == ci) {
+			play_fire_sound(ITEM_FAN + WEAPONS, x, 255, 1000);
+
+			if (a < 180)
+				// move wind to the right
+				global.wind += (p / 20);
+			else
+				// wind to the left
+				global.wind -= (p / 20);
+
+			// make sure wind is not too strong
+			if (global.wind < (-env.windstrength / 2) )
+				global.wind = -env.windstrength / 2;
+			else if (global.wind > (env.windstrength / 2) )
+				global.wind = env.windstrength / 2;
+
+			global.lastwind = global.wind;
+		}
+		// --- Vengeance (BOOM BABY!) ---
+		//--------------------------------
+		else if ( (ITEM_VENGEANCE <= ci) && (ITEM_FATAL_FURY >= ci) ) {
+			// Just preparation. The tank explodes, and the vengeance goes
+			// off automatically as selected. ;-)
+			this->player->reclaimShield();
+			this->addDamage(nullptr, l + sh + repair_rate + 1);
+			this->applyDamage();
+		}
+	} // End of items
+
+	player->time_left_to_fire = env.maxFireTime;
 }
 
 
-
-void TANK::Destroy()
+/// @brief adds damage, sets creditTo and handles pending damage
+void TANK::addDamage(PLAYER* damageFrom, double damage_)
 {
-  #ifdef NETWORK
-  if (player)    // we should always have a player, but better safe than sorry
-  {
-      int player_index = 0;
-      bool found = false;
-      char buffer[64];
-      while ( (player_index < _global->numPlayers) && (!found) )
-      {
-          // get the player index
-          if ( ( _global->players[player_index]->tank ) && (_global->players[player_index]->tank == this) )
-              found = true;
-          else
-              player_index++;
-     }
-     if (found)        // we should have found a match and now we send it to all clients
-     {
-         sprintf(buffer, "REMOVETANK %d", player_index);
-         _global->Send_To_Clients(buffer);
-     }
-  }
-  #endif
-
-   if (_global && player)
-    {
-      int random_item;
-      if (_global->violent_death)
-        {
-          random_item = rand() % VIOLENT_CHANCE;
-          if ( ( random_item > _global->violent_death ) && (random_item <= VIOLENT_DEATH_HEAVY) )
-            random_item = (int) _global->violent_death;
-
-          switch (random_item)
-            {
-            case VIOLENT_DEATH_LIGHT:
-              player->ni[ITEM_VENGEANCE] += 1;
-              break;
-            case VIOLENT_DEATH_MEDIUM:
-              player->ni[ITEM_DYING_WRATH] += 1;
-              break;
-            case VIOLENT_DEATH_HEAVY:
-              player->ni[ITEM_FATAL_FURY] += 1;
-              break;
-            }     //end of switch
-        }
-
-        if (player->ni[ITEM_FATAL_FURY] > 0)
-        {
-          int numLaunch = (int)item[ITEM_FATAL_FURY].vals[SELFD_NUMBER];
-          cw = (int)item[ITEM_FATAL_FURY].vals[SELFD_TYPE];
-          player->ni[ITEM_FATAL_FURY]--;
-          player->nm[cw] += numLaunch;
-          for (int count = numLaunch; count > 0; count--)
-            {
-              a = rand () % 180 + 90;
-              p = rand () % (MAX_POWER / 2);
-              activateCurrentSelection ();
-            }
-        }
-        else if (player->ni[ITEM_DYING_WRATH] > 0)
-        {
-          int numLaunch = (int)item[ITEM_DYING_WRATH].vals[SELFD_NUMBER];
-          cw = (int)item[ITEM_DYING_WRATH].vals[SELFD_TYPE];
-          player->ni[ITEM_DYING_WRATH]--;
-          player->nm[cw] += numLaunch;
-          for (int count = numLaunch; count > 0; count--)
-            {
-              a = rand () % 180 + 90;
-              p = rand () % (MAX_POWER / 2);
-              activateCurrentSelection ();
-            }
-        }
-        else if (player->ni[ITEM_VENGEANCE] > 0)
-        {
-          int numLaunch = (int)item[ITEM_VENGEANCE].vals[SELFD_NUMBER];
-          cw = (int)item[ITEM_VENGEANCE].vals[SELFD_TYPE];
-          player->ni[ITEM_VENGEANCE]--;
-          player->nm[cw] += numLaunch;
-          for (int count = numLaunch; count > 0; count--)
-            {
-              a = rand () % 180 + 90;
-              p = rand () % (MAX_POWER / 2);
-              activateCurrentSelection ();
-            }
-        }
-    }
-}
+	// Clear pending damage if the 'deliverer' changes
+	if (damageFrom != creditTo) {
+		applyDamage();
+		damage = 0.;
 
+		// Update creditTo first
+		creditTo   = damageFrom;
+		newDamager = true;
+	}
 
+	assert( (damage_ >= 0.) && "ERROR: Negative damage?");
 
-void TANK::update ()
-{
-  VIRTUAL_OBJECT::update ();
+	// Raise damage
+	if (damage_ > 0.)
+		damage += damage_;
 }
 
-void TANK::requireUpdate ()
-{
-  VIRTUAL_OBJECT::requireUpdate ();
-}
 
 void TANK::applyDamage ()
 {
-  char buf[10];
-  FLOATTEXT *damageText;
-  FLOATTEXT *revengeText;
-  FLOATTEXT *gloatText;
-  FLOATTEXT *suicideText;
-  char *temp_text;
-  bool killed = false;
-
-  if (damage > sh + l)
-    {
-      damage = sh + l;
-      killed = true;
-      player->killed++;
-    }
-  sh -= (int)damage;
-  if (creditTo)
-    {
-      if (player != creditTo)  	//enemy hit ++
-        {
-          if ( killed )
-            creditTo->kills++;
-          creditTo->money += (int)(damage * _global->scoreHitUnit);
-          if (creditTo->tank)
-          {
-                char the_money[64];
-                sprintf(the_money, "$%s", Add_Comma( (int) (damage * _global->scoreHitUnit) ) );
-                // show how much the shooter gets
-                FLOATTEXT *moneyText = new FLOATTEXT(_global, _env, the_money,
-                                    (int) creditTo->tank->x, (int) creditTo->tank->y - 30,
-                                    makecol(0, 255, 0), CENTRE);
-                if (moneyText)
-                {
-                      // moneyText->xv = 0;
-                      // moneyText->yv = -0.5;
-                      moneyText->set_speed(0.0, -0.5);
-                      moneyText->maxAge = 200;
-                }
-                
-          }
-          // this tank is destroyed, the attacker gloats
-          if ( (killed) && (! creditTo->gloating) )
-            {
-              // avoid trying to print victory message over a dead tank
-              if (creditTo->tank)
-                {
-                  temp_text = creditTo->selectGloatPhrase();
-                  gloatText = new FLOATTEXT (_global, _env,
-                                             // creditTo->selectGloatPhrase( (double) damage / maxLife),
-                                             temp_text,
-                                             (int) creditTo->tank->x, (int) creditTo->tank->y - 30,
-                                             creditTo->color, CENTRE);
-                  if (gloatText)
-                  {
-                    // gloatText->xv = 0;
-                    // gloatText->yv = -0.4;
-                    gloatText->set_speed(0.0, -0.4);
-                    gloatText->maxAge = 300;
-                  }
-                  else
-                      perror ( "tank.cc: Failed allocating memory for gloatText in applyDamage.");
-                  creditTo->gloating = true;
-                }
-            }
-
-          if ((int)player->type != HUMAN_PLAYER)
-            {
-              if (player->revenge == creditTo)
-                {
-                  player->annoyanceFactor += damage;
-                }
-              else
-                {
-                  player->annoyanceFactor = damage;
-                }
-              if (  (player->annoyanceFactor > (player->vengeanceThreshold * maxLife) )
-                  &&((rand() % 100) <= player->vengeful) )
-                {
-                  player->revenge = creditTo;
-                  temp_text = player->selectRevengePhrase();
-                  revengeText = new FLOATTEXT (_global, _env,
-                                               // player->selectRevengePhrase ((double)damage / maxLife),
-                                               temp_text,
-                                               (int)x, (int)y - 30,
-                                               player->color, CENTRE);
-                  if (revengeText)
-                  {
-                    // revengeText->xv = 0;
-                    // revengeText->yv = -0.4;
-                    revengeText->set_speed(0.0, -0.4);
-                    revengeText->maxAge = 300;
-                  }
-                  else
-                      perror ( "tank.cc: Failed allocating memory for revengeText in applyDamage");
-                }
-            }
-        }
-      else  	//self hit --
-        {
-          if ( (creditTo->money - (damage * _global->scoreSelfHit)) < 0)
-            creditTo->money = 0;
-          else
-            creditTo->money -= (int)(damage * _global->scoreSelfHit);
-
-          if (damage >= (l + sh) )
-            {
-              temp_text = player->selectSuicidePhrase();
-              suicideText = new FLOATTEXT (_global, _env,
-                                           player->selectSuicidePhrase(),
-                                           (int) x, (int) y - 30,
-                                           player->color, CENTRE);
-              if (suicideText)
-              {
-                 // suicideText->xv = 0;
-                 // suicideText->yv = -0.4;
-                 suicideText->set_speed(0.0, -0.4);
-                 suicideText->maxAge = 300;
-              }
-              else
-                  perror ( "tank.cc: Failed allocating memory for suicideText in applyDamage.");
-            }
-        }
-    }
-
-  if (sh < 0)
-    l += sh;
-  if (l < 0)
-    l = 0;
-  if (sh <= 0)
-    {
-      repulsion = 0;
-      shieldColor = 0;
-      shieldThickness = 0;
-      sh = 0;
-    }
-
-  if ((int)damage > 0)
-    {
-      flashdamage = 1;
-      sprintf (buf, "%d", (int)damage);
-      damageText = new FLOATTEXT (_global, _env,
-                                  NULL, (int)x, (int)y,
-                                  makecol (255, 0, 0), CENTRE);
-      if (damageText)
-      {
-         damageText->set_text(buf);
-         // damageText->xv = 0;
-         // damageText->yv = -0.2;
-         damageText->set_speed(0.0, -0.2);
-         damageText->maxAge = 300;
-         // damageText->sway = NORMAL_SWAY;
-      }
-      else
-          perror ( "tank.cc: Failed allocating memory for damageText in TANK::TANK");
-    }
-  if (sh > 0)
-    {
-      sprintf (buf, "%d", sh);
-      shieldText->set_text (buf);
-    }
-  else
-    {
-      shieldText->set_text (NULL);
-    }
-  sprintf (buf, "%d", l);
-  healthText->set_text (buf);
+	// Only *one* call to applyDamage() at any time!
+	std::lock_guard<CSpinLock> apply_damage_lock(damage_lock);
+
+	// Before taking any action, damage must be at least 1
+	if (destroy || (damage < 1.)) {
+		newDamager = false;
+		return;
+	}
+
+	// See if the pending damage destroys the tank:
+	int32_t full_damage = ROUND(damage);
+
+	if (full_damage >= (sh + l) ) {
+		destroy     = true;
+		damage      = sh + l; // Don't overdo
+		full_damage = damage;
+		player->killed++;
+	}
+
+	// Damage is applied to the shield, and negative shield later
+	// added to life. This saves some if/then/else constructs
+	int32_t old_sh = sh; // For repulsor shield blast through
+	sh -= full_damage;
+
+	/* -----------------------------------------------------------
+	 * --- If a damage dealer is set, they must be awarded     ---
+	 * --- their reward or, in case of self/team hit, penalty. ---
+	 * -----------------------------------------------------------
+	 */
+	if (creditTo) {
+		int32_t award    = full_damage;
+		bool    self_hit = creditTo == player;
+		bool    team_hit =  !self_hit
+		                 && (TEAM_NEUTRAL != creditTo->team)
+		                 && (player->team == creditTo->team);
+
+		// Award kill point if no suicide
+		if (destroy && !self_hit)
+			creditTo->kills++;
+
+		// note damage in own and opponents memory
+		player->noteDamageFrom(creditTo, full_damage, destroy);
+		creditTo->noteDamageTo(player,   full_damage, destroy);
+
+		// The award must be adapted to the situation
+		award *= self_hit ? env.scoreSelfHit :
+		         team_hit ? env.scoreTeamHit :
+		         env.scoreHitUnit;
+
+		// The kill bonus is only applied if this is no team kill
+		if (destroy && !team_hit)
+			award += self_hit
+			         ? env.scoreUnitSelfDestroy
+			         : env.scoreUnitDestroyBonus;
+
+		// Money can not go negative.
+		if ((self_hit || team_hit) && (award > creditTo->money))
+			award = creditTo->money;
+
+		// If there is an award now, get the money text out
+		// and the register to ring.
+		if (award > 0) {
+			if (creditTo->tank && !global.skippingComputerPlay) {
+				static char the_money[16] = { 0x0 };
+				snprintf(the_money, 15, "%s$%s",
+						(team_hit || self_hit) ? "-" : "",
+						Add_Comma( award ) );
+				// show how much the shooter gets
+				try {
+					new FLOATTEXT(the_money,
+							creditTo->tank->x, creditTo->tank->y - 30,
+							.0, -.5, team_hit ? PURPLE : self_hit ? RED : GREEN,
+							CENTRE,
+							env.swayingText ? TS_HORIZONTAL : TS_NO_SWAY,
+							200);
+					if (global.stage < STAGE_SCOREBOARD)
+						global.updateMenu = true;
+				} catch (...) {
+					perror ( "tank.cpp: Failed allocating memory for money text"
+							 " in applyDamage().");
+				}
+			}
+			creditTo->money += ( (team_hit || self_hit) ? -1 : 1) * award;
+		} // End of applying damage award
+
+		// If the tank is destroyed, and it was neither self nor team hit,
+		// the damager might spawn a gloating message
+		if ( destroy && !creditTo->gloating && !team_hit && !self_hit
+		  && creditTo->tank && !creditTo->tank->destroy
+		  && !global.skippingComputerPlay) {
+
+			creditTo->gloating = true;
+
+			try {
+				new FLOATTEXT(creditTo->selectGloatPhrase(),
+							creditTo->tank->x, creditTo->tank->y - 30,
+							.0, -.4, creditTo->color, CENTRE, TS_NO_SWAY, 200);
+			} catch (...) {
+				perror ( "tank.cpp: Failed to allocate memory for"
+						 " gloating text in applyDamage().");
+			}
+		} // End of spawning gloating text
+
+		// Issue a suicide message if the player applied for a darwin award
+		if (self_hit && destroy && !global.skippingComputerPlay) {
+			try {
+				new FLOATTEXT(player->selectSuicidePhrase(),
+								x, y - 30, .0, -.4, player->color,
+								CENTRE, TS_NO_SWAY, 300);
+			} catch (...) {
+				perror ( "tank.cpp: Failed allocate memory for suicide"
+						 " text in applyDamage().");
+			}
+		}
+	} // End of handling damager texts and awards
+
+	// -----------------------------------
+	// --- Eventually apply the damage ---
+	// -----------------------------------
+	int32_t old_life = l;
+	if (destroy) {
+		sh = 0;
+		l  = 0;
+		repulsion = 0;
+	} else {
+		if ( (ITEM_LGT_REPULSOR_SHIELD <= sht)
+		  && (ITEM_HVY_REPULSOR_SHIELD >= sht) ) {
+			// Repulsor shields do not take the full damage but
+			// let half of it blasted through.
+			int32_t sh_dmg = full_damage / 2;
+			int32_t t_dmg  = (full_damage / 2) + (full_damage % 2);
+			if (sh_dmg >= old_sh) {
+				t_dmg += sh_dmg - old_sh;
+				sh_dmg = old_sh;
+			}
+
+			sh = old_sh - sh_dmg;
+			l -= t_dmg;
+
+		}
+
+		// normal shield-is-empty-check:
+		if (sh < 0.5) {
+			l += sh; // For non-repulsor shields.
+			sh = 0;
+			repulsion = 0;
+		}
+	}
+
+	// --------------------------------
+	// --- Display the damage value ---
+	// --------------------------------
+	if (full_damage > 0) {
+		static char buf[10] = { 0x0 };
+		flashdamage = 1;
+
+		if (!global.skippingComputerPlay) {
+			snprintf (buf, 9, "%d", full_damage);
+			try {
+				new FLOATTEXT(buf, x, y - 30, .0, -.3, RED, CENTRE,
+						env.swayingText ? TS_HORIZONTAL : TS_NO_SWAY, 300);
+			} catch (...) {
+				perror ( "tank.cpp: Failed to allocate memory for damage"
+						 " text in applyDamage().");
+			}
+		}
+
+		// If shield remains, the shield text has to be regenerated
+		if (sh > 0) {
+			snprintf (buf, 9, "%d", sh);
+			shieldText.set_text (buf);
+		} else
+			shieldText.set_text (nullptr);
+
+		// If life points were taken, the life text is to be regenerated
+		if (old_life != l) {
+			snprintf (buf, 9, "%d", l);
+			healthText.set_text (buf);
+		}
+	} // End of having damage
+
+  damage = 0.; // all applied
 }
 
-void TANK::framelyAccounting ()
+
+// Thanks to the rockets, tanks can 'fly', and thanks to ... uhm ...
+// everything, tanks might fall down.
+void TANK::applyPhysics ()
 {
-  /*
-  if (shPhase < 0)
-    shPhase = rand () % 360;
-  shPhase += (int)(item[sht].vals[SHIELD_ENERGY] / sh) + 10;
-  while (shPhase >= 360)
-    shPhase -= 360;
-  */
-  shPhase = shPhase + delta_phase;
-  if ( ( shPhase > 5) || (shPhase < -2) )
-     delta_phase = -delta_phase;
+	// Do nothing if this tank was destroyed
+	if (destroy)
+		return;
+
+	double old_y = y;
+
+	// Special handling when rocketing upwards first:
+	if ( yv < 0. ) {
+		// Although activating a rocket instantly pushes the tank
+		// upwards, it stops there if the tank is buried
+		if ( howBuried(nullptr, nullptr) ) {
+			xv = 0;
+			yv = 0;
+		}
+
+		/// @todo : Is this really a problem?
+		// make sure the tank does not leave the screen when flying
+		else if ( (y < tank_off_y) )
+			yv = 0;
+
+		// Otherwise apply rocket x movement
+		else
+			x += xv * 4.;
+	} // End of special rocketing handling
+
+	// General movement is only applied while no damage flashes.
+	// Note: This means that damage application halts all movement.
+	if (flashdamage)
+		++flashdamage; // Frame counted
+	else {
+		bool          on_tank = tank_on_tank();
+		int32_t       bottom  = env.screenHeight - tank_off_y; // shortcut
+		int32_t pix_col = getpixel(global.terrain, x, y + tank_off_y - tank_sag);
+
+		// Hitting a wall only bounces the tank.
+		if ( ((x + xv) < 1) || ((x + xv) > (env.screenWidth - 1)) )
+			xv *= -1.;
+
+		// Check whether a previous fall just ends:
+		if ( (yv > 0.) && ( (y >= bottom) || (PINK != pix_col) || on_tank ) ) {
+			if (isTeleported) {
+				addDamage(creditTo, yv * 10.);
+				isTeleported = false;
+			} else
+				addDamage(nullptr, yv * 10.);
+
+			// 10 points of damage are 'free' when falling
+			// Note: This negates any damage when parachuting as well.
+			if (damage >= 10.)
+				damage -= 10.;
+			else
+				damage  = 0.;
+
+			// Stop movement
+			xv = 0.;
+			yv = 0.;
+
+			// fix y if the tank was fast enough to push through the floor
+			if (y > bottom)
+				y = bottom;
+
+			// Reset falling delay and apply damage at once
+			delay_fall = env.landSlideDelay * 100;
+			applyDamage();
+		} // End of fall stop
+
+		// Check whether the tank currently is falling
+		else if ( (y < bottom) && (PINK == pix_col) && !on_tank
+				&& (env.landSlideType > SLIDE_NONE) ) {
+
+			// If this is set to cartoon falling, decrease delay and exit.
+			if ( (SLIDE_CARTOON == env.landSlideType)
+			  && (--delay_fall > 0) )
+				return;
+
+			yv += env.gravity * env.FPS_mod;
+
+			// Check for parachute opening
+			if (para) {
+				if (para < 3)
+					++para;
+
+				// With a parachute, wind can blow the tank away
+				xv += (global.wind - xv) / mass
+				    * (drag + 0.35) * env.viscosity;
+
+				// Limit yv, we have a parachute!
+				if (yv > 0.5 )
+					yv = 0.5;
+			} else {
+				// If we have parachutes, deploy one:
+				if ((player->ni[ITEM_PARACHUTE]) && (yv >= 1.0)) {
+					para = 1;
+					player->ni[ITEM_PARACHUTE]--;
+				}
+			}
+
+			x += xv;
+			y += yv;
+		} // End of fall start / continue
+
+		// Nothing? Then make sure no parachute is shown
+		else
+			para = 0;
+
+		// If there is no damage flashing, apply what is there
+		if (!flashdamage) {
+			applyDamage();
+		}
+
+		requireUpdate();
+	} // End of regular movement
+
+	setTextPositions(old_y != y);
 }
 
-int TANK::applyPhysics ()
+
+/// @brief Test if the current weapon is available. Find another one,
+/// preferably stronger, if the current is empty.
+void TANK::check_weapon()
 {
-  int stable = 0;
-  int landed_on_tank;
-
-  // if we are buried, rockets shouldn't work
-  if ( howBuried() )
-  {
-    xv = 0;
-    if (yv < 0)
-       yv = 0;
-  }
-
-  // make sure the tank does not leave the screen when flying
-  if ( (yv < 0) && (y < TANKHEIGHT) )
-     yv = 0;
-
-  if (yv < 0)
-      x += xv * 4;
-
-  if (!flashdamage)
-    {
-      if (x + xv < 1 || x + xv > (_global->screenWidth-1))
-        xv = -xv;	//bounce on the border
-      int pixelCol = getpixel (_env->terrain, (int)x, (int)y + (TANKHEIGHT - TANKSAG));
-
-      // check to see if we have landed on another tank -- Jesse
-      landed_on_tank = tank_on_tank ( _global );
-
-      // we are falling and have hit the bottom of the screen or fallen onto dirt or on a tank
-      if ((l > 0) && (yv > 0) && ((y >= _global->screenHeight - TANKHEIGHT) ||
-                                  (pixelCol != PINK) ||
-                                  (landed_on_tank) ))
-        {
-          //count damage and add money
-          damage = (int) (yv * 10);
-          if (damage >= 10)
-            damage -= 10;
-          else damage = 0;
-          creditTo = NULL;
-          yv = 0;
-          xv = 0;
-          // if we passed the bottom, then stop on bottom
-          if (y > _global->screenHeight - TANKHEIGHT)
-            y = _global->screenHeight - TANKHEIGHT;
-
-          // delay_fall = GRAVITY_DELAY;
-          delay_fall = (int) _env->landSlideDelay * 100;
-
-        }
-
-      // the tank is falling
-      else if ((y < _global->screenHeight - TANKHEIGHT) && (pixelCol == PINK) && (l > 0) &&
-               (! landed_on_tank) && (_env->landSlideType > LANDSLIDE_NONE) )
-        {
-          delay_fall--;
-          if ( (delay_fall > 0) && (_env->landSlideType == LANDSLIDE_CARTOON) )
-            return stable;
-
-          #ifdef OLD_GAMELOOP
-          if (para && para < 3 && !_env->pclock)
-            para++;
-          #else
-          if ( (para) && (para < 3) )
-            para++;
-          #endif
-          if (!para)
-            {
-              yv += _env->gravity * (100.0 / _global->frames_per_second);
-              y += yv;
-            }
-          else
-            {
-              double accel = (_env->wind - xv) / mass * (drag + 0.35) * _env->viscosity;
-              xv += accel;
-              yv += _env->gravity * (100.0 / _global->frames_per_second);
-              if (yv > 0.5 )
-                yv = 0.5;
-              x += xv;
-              y += yv;
-            }
-          // falling, deploy parachute
-          if (!para)
-            {
-              if ((player->ni[ITEM_PARACHUTE]) && (yv >= 1.0))
-                {
-                  _env->pclock = 1;
-                  para = 1;
-                  player->ni[ITEM_PARACHUTE]--;
-                }
-
-            }
-          requireUpdate ();
-        }
-      else
-        {
-          stable = 1;
-          if (damage && !pen)
-            {
-              applyDamage ();
-
-              pen = 1;
-            }
-          para = 0;
-        }
-    }
-  else
-    {
-      flashdamage++;
-    }
-  return (stable);
+	if ( (cw < 0) || (cw > WEAPONS) )
+		cw = 0;
+
+	if (player && !player->nm[cw] ) {
+		// Try upwards first:
+		int32_t old_cw = cw++;
+		for ( ; (cw < WEAPONS) && !player->nm[cw] ; ++cw) ;
+
+		// If this wasn't successful, go downwards:
+		if (WEAPONS == cw) {
+			cw = old_cw - 1;
+			for ( ; (cw > 0) && !player->nm[cw] ; --cw) ;
+		}
+
+		// This ends up with Small Missile if no other weapons are available.
+		player->changed_weapon = true;
+	}
 }
 
-void TANK::explode ()
+
+/// @brief Deactivate vertical bounce and reset text positions
+void TANK::deactivate()
 {
-  FLOATTEXT *revengeText;
-  EXPLOSION *explosion;
-  char *temp_text;
-
-  if ((int)player->type != HUMAN_PLAYER)
-    {
-      player->revenge = creditTo;
-      temp_text = player->selectRevengePhrase();
-      revengeText = new FLOATTEXT (_global, _env,
-                                   temp_text,
-                                   (int)x, (int)y - 30,
-                                   player->color, CENTRE);
-      if (revengeText)
-      {
-          // revengeText->xv = 0;
-          // revengeText->yv = -0.4;
-          revengeText->set_speed(0.0, -0.4);
-          revengeText->maxAge = 300;
-      }
-      else
-          perror ( "tank.cc: Failed allocating memory for revengeText in TANK::explode");
-    }
-  explosion = new EXPLOSION (_global, _env, x, y, 1);
-  if (explosion)
-  {
-     explosion->player = player;
-     explosion->bIsWeaponExplosion = false;
-  }
-  else
-      perror ( "tank.cc: Failed allocating memory for explosion in TANK::explode");
-  
-
-  destroy = TRUE;
-  play_sample ((SAMPLE *)_global->sounds[WEAPONSOUNDS], _env->scaleVolume(255), 128, 1000, 0);
+	shieldText.set_sway_type(TS_NO_SWAY);
+	healthText.set_sway_type(TS_NO_SWAY);
+	nameText.set_sway_type(TS_NO_SWAY);
+
+	setTextPositions(true);
 }
 
-void TANK::repulse (double xpos, double ypos, double *xaccel, double *yaccel, int aWeaponType)
+
+void TANK::draw()
 {
-  if (repulsion != 0)
-    {
-      double xdist = xpos - x;
-      double ydist = -1.0 * fabs(ypos - y);
-
-      if ((xdist < 0.1) && (xdist > -0.1)) xdist = 0.1;
-      if ((ydist < 0.1) && (ydist > -0.1)) ydist = -0.1; // Assume missile comes from above
-
-      if (	(aWeaponType >= BURROWER) && (aWeaponType <= PENETRATOR)
-           &&(ypos > y)	)
-        ydist *= -1.0; // they normally come from below!
-
-      double distance2 = (xdist * xdist) + (ydist * ydist);
-      double distance = sqrt (distance2);
-
-      if (distance < (60.0 + sqrt ((double)repulsion)))
-        {
-          *xaccel = repulsion * (xdist / distance) / distance2;
-          *yaccel = repulsion * (ydist / distance) / distance2;
-        }
-    }
+	// check for foggy weather
+	if ( ( env.fog ) && ( global.get_curr_tank() != this ) ) {
+		addUpdateArea (x - tank_off_x - 3, y - 25, 35, 46);
+		requireUpdate ();
+		return;
+	}
+
+	// get bitmap for tank
+	if ( (use_tankbitmap < 0) || (use_turretbitmap < 0) ) {
+		setBitmap();
+
+		assert ((use_tankbitmap >= 0) && (use_turretbitmap >= 0)
+			&& "ERROR: Unable to set tank/turret bitmap!");
+
+		// No bitmap, no fun
+		if ( (use_tankbitmap < 0) || (use_turretbitmap < 0) )
+			return;
+	}
+
+	// Put a coloured rectangle below the tank.
+	rectfill (global.canvas, x - tank_off_x, y + tank_off_y,
+	          x + tank_off_x, y + tank_off_y + 2,
+	          this->player->color);
+
+	// Draw shield first if any
+	if (sh > 0) {
+		// Make sure the values are set. (Client may not have set them)
+		assert((BLACK != shld_col_outer) && shld_thickness
+				&& "ERROR: Did client() forget to set shield values?");
+
+		if (BLACK == shld_col_outer)
+			shld_col_outer = makecol(item[sht].vals[SHIELD_RED],
+			                         item[sht].vals[SHIELD_GREEN],
+			                         item[sht].vals[SHIELD_BLUE]);
+		if (!shld_thickness)
+			shld_thickness = item[sht].vals[SHIELD_THICKNESS];
+
+		// Adapt shield phase:
+		double str_mod = static_cast<double>(sh) / item[sht].vals[SHIELD_ENERGY];
+		// The weaker the shield, the faster the phase
+		shld_phase += shld_delta / str_mod;
+		// Don't overdo
+		while (shld_phase > 360.)
+			shld_phase -= 360.;
+
+		// Set basic values
+		int32_t move_x  = ROUNDu(x);
+		int32_t move_y  = ROUNDu(y) - turr_off_y + shld_rad_y;
+		int32_t rad_x   = shld_rad_x;
+		int32_t rad_y   = shld_rad_y;
+		int32_t half_th = shld_thickness / 2;
+
+		// Whether it is done over x or y depends on the type of shield
+		if (sht < ITEM_LGT_REPULSOR_SHIELD) {
+			rad_x  += static_cast<int32_t>(env.slope[ROUNDu(shld_phase)][0] * 3.);
+			rad_y  += half_th;
+			move_x -= half_th;
+		} else {
+			rad_x  += half_th;
+			rad_y  += shld_phase * 6. / 360.;
+			move_y -= half_th;
+		}
+
+		drawing_mode(DRAW_MODE_TRANS, nullptr, 0, 0);
+		global.current_drawing_mode = DRAW_MODE_TRANS;
+		set_trans_blender (0, 0, 0, 50);
+		ellipsefill (global.canvas, move_x, move_y, rad_x, rad_y, shld_col_inner);
+		set_trans_blender (0, 0, 0, ROUND(200. * str_mod) + 25);
+
+		if (sht < ITEM_LGT_REPULSOR_SHIELD) {
+			for (int32_t i = 0; i < shld_thickness; ++i)
+				ellipse (global.canvas, move_x + i, move_y, rad_x, rad_y, shld_col_outer);
+		} else {
+			for (int32_t i = 0; i < shld_thickness; ++i)
+				ellipse (global.canvas, move_x, move_y + i, rad_x, rad_y, shld_col_outer);
+		}
+
+		drawing_mode(DRAW_MODE_SOLID, nullptr, 0, 0);
+		global.current_drawing_mode = DRAW_MODE_SOLID;
+		setUpdateArea (move_x - shld_thickness - rad_x,
+		               move_y - shld_thickness - rad_y,
+		               (rad_x + shld_thickness) * 2,
+		               (rad_y + shld_thickness) * 2);
+	} // End of drawing shield
+
+	// Without a shield, the update area can be smaller
+	else
+		setUpdateArea (x - turr_off_x - 1, y - turr_off_x - 1,
+		               (turr_off_x * 2) + 2, tank_off_y + turr_off_x + 20);
+
+	// Now draw the tank sprite
+	draw_sprite   (global.canvas, env.tank[use_tankbitmap], x - tank_off_x, y);
+	rotate_sprite (global.canvas, env.tankgun[use_turretbitmap],
+					x - turr_off_x, y - turr_off_y,
+					itofix( (90 - a) * 256 / 360) );
+
+	// when using rockets, show flame
+	if (yv < 0)
+		/// @todo : This looks silly. We sure can do better, can't we?
+		rectfill(global.canvas, x - tank_off_x, y + tank_off_y,
+		               x + tank_off_x, y + tank_off_y + 10, ORANGE);
+
+	// Eventually draw the parachute
+	if (para) {
+		draw_sprite (global.canvas, env.tank[para], x - tank_off_x - 3, y - 25);
+		addUpdateArea (x - tank_off_x - 3, y - 25, 35, 66);
+	}
+
+	setTextPositions(false);
+	requireUpdate ();
 }
 
-void TANK::printHealth (int offset)
+
+/// @brief Create explosion and sound if a tank is destroyed. If available
+/// and/or set, stage a violent death.
+void TANK::explode (bool allow_vengeance)
 {
-  int textpos = -5;
-
-  shieldText->set_pos ((int)x, (int)y - TANKHEIGHT + textpos + offset);
-  if (sh > 0)
-    textpos -= 10;
-  healthText->set_pos ((int)x, (int)y - TANKHEIGHT + textpos + offset);
-
-  // display player name
-  if (nameText)
-  {
-      textpos -= 10;
-      nameText->set_pos ((int)x, (int)y - TANKHEIGHT + textpos + offset);
-  }
+	if (!destroy)
+		return;
+
+	// Note: player->revenge and revenge texts are handled in applyDamage()
+
+	try {
+		new EXPLOSION (player, x, y, 0., env.screenHeight / 10., MED_MIS, false);
+	} catch (...) {
+		perror ( "tank.cpp: Failed to allocate memory for explosion"
+		         " in TANK::explode()");
+	}
+
+	play_explosion_sound(MED_MIS, x, 255, 1000);
+
+	// Violent death can only trigger if there is a player.
+	// Note: There should be, always, so assert as well. Calling this method
+	// after player was set to nullptr is a bug.
+	assert (player && "ERROR: explode() called with nullptr player!");
+	if (nullptr == player)
+		return;
+
+#ifdef NETWORK
+	int32_t     playerindex = 0;
+	bool        found       = false;
+	static char buffer[15]  = { 0x0 };
+
+	// get the player index
+	while ( (playerindex < env.numGamePlayers) && (!found) ) {
+		if (  env.players[playerindex]
+		  && (env.players[playerindex]->tank == this) )
+			found = true;
+		else
+			playerindex++;
+	}
+
+	// we should have found a match and now we send it to all clients
+	if (found) {
+		snprintf(buffer, 14, "REMOVETANK %d", playerindex);
+		env.sendToClients(buffer);
+	}
+#endif // NETWORK
+
+	// If vengeance is not allowed, break up here.
+	if (!allow_vengeance)
+		return;
+
+	// If violent death is enabled to be automatically,
+	// possibly sponsor one for the player unless they
+	// already have something better.
+	// But only if it is not the first 3 rounds.
+	if (env.violent_death && ( (env.rounds - global.currentround) > 3) ) {
+		int32_t ri = rand() % VIOLENT_CHANCE;
+
+		// Limit ri to the value of violent_death.
+		// This makes it less probable to trigger anything on lower settings.
+		if ( (ri <= VD_HEAVY) && (ri > env.violent_death) )
+			ri = env.violent_death;
+
+		// The entry is, that the player either has no fatal fury
+		// or is about to get one sponsored.
+		if (ri && (!player->ni[ITEM_FATAL_FURY] || (VD_HEAVY == ri)) ) {
+			if (VD_HEAVY == ri)
+				++player->ni[ITEM_FATAL_FURY];
+			else if (!player->ni[ITEM_DYING_WRATH] || (VD_MEDIUM == ri)) {
+				if (VD_MEDIUM == ri)
+					++player->ni[ITEM_DYING_WRATH];
+				else if (VD_LIGHT == ri)
+					++player->ni[ITEM_VENGEANCE];
+			}
+		} // end of applying sponsorship to big boom
+	} // End of violent death sponsorship
+
+	// If the player has something to trigger in their last moment,
+	// do so now:
+	int32_t numLaunch = 0;
+	int32_t min_power = 0;
+	int32_t del_power = 0;
+
+	if (player->ni[ITEM_FATAL_FURY] > 0) {
+		numLaunch = item[ITEM_FATAL_FURY].vals[SELFD_NUMBER];
+		cw        = item[ITEM_FATAL_FURY].vals[SELFD_TYPE];
+		player->nm[cw] += numLaunch;
+		player->ni[ITEM_FATAL_FURY]--;
+		min_power = MAX_POWER / 3;
+		del_power = MAX_POWER / 3;
+	} else if (player->ni[ITEM_DYING_WRATH] > 0) {
+		numLaunch = item[ITEM_DYING_WRATH].vals[SELFD_NUMBER];
+		cw        = item[ITEM_DYING_WRATH].vals[SELFD_TYPE];
+		player->nm[cw] += numLaunch;
+		player->ni[ITEM_DYING_WRATH]--;
+		min_power = MAX_POWER / 4;
+		del_power = MAX_POWER / 3;
+	} else if (player->ni[ITEM_VENGEANCE] > 0) {
+		numLaunch = item[ITEM_VENGEANCE].vals[SELFD_NUMBER];
+		cw        = item[ITEM_VENGEANCE].vals[SELFD_TYPE];
+		player->nm[cw] += numLaunch;
+		player->ni[ITEM_VENGEANCE]--;
+		min_power = MAX_POWER / 5;
+		del_power = MAX_POWER / 3;
+	}
+
+	// If there is anything to launch, do it
+	if (numLaunch) {
+		// Expensive equipment like this should come with a certain quality.
+		// The most important detail (right after actually going off and not
+		// being a dud) is that the bucks won't be blasted the wrong way.
+		TANK* tank    = nullptr;
+		int32_t med_x = 0;
+		int32_t tanks = 0;
+
+		global.getHeadOfClass(CLASS_TANK, &tank);
+
+		while (tank) {
+			if ( (tank != this) && !tank->destroy
+			  && ( (TEAM_NEUTRAL == player->team)
+			    || (player->team != tank->player->team) ) ) {
+				++tanks;
+				med_x += tank->x;
+			}
+			tank->getNext(&tank);
+		}
+
+		// Get the medium x position of all tanks (or the middle of the
+		// screen if this was the last tank... for the firework...)
+		if (tanks)
+			med_x /= tanks;
+		else
+			med_x = env.halfWidth;
+
+		int32_t start_a = 45;
+		int32_t mod_a   = 90;
+
+		if (med_x < (x - 50) ) {
+			start_a = 30;
+			mod_a   = 55;
+		} else if (med_x > (x + 50) ) {
+			start_a = 95;
+			mod_a   = 55;
+		}
+
+		// Before the violent death is applied, halve the players
+		// damage multiplier:
+		assert(player && "ERROR: explode Tank without player?");
+		player->damageMultiplier = player->damageMultiplier > 1.
+		                         ? 1. + ((player->damageMultiplier - 1.) / 2.)
+		                         : .75;
+
+		// Now go for it!
+		int32_t cur_stage = global.stage;
+		global.stage = STAGE_FIRE;
+		for (int32_t i = numLaunch; i > 0; --i) {
+			a = 180 - (start_a + (rand() % mod_a) - 90);
+			p = min_power + (rand () % del_power);
+			activateCurrentSelection ();
+		}
+		global.stage = cur_stage;
+	}
 }
 
-void TANK::drawTank (BITMAP *dest, int healthOffset)
+
+/// @return The tanks bottom coordinate as used in collision detection.
+int32_t TANK::getBottom()
 {
-  int turretAngle;
-
-  // check for foggy weather
-  if ( ( _env->fog ) && ( _global->currTank != this ) )
-    {
-      addUpdateArea ((int)(x - TANKWIDTH) - 3, (int)y - 25, 35, 46);
-      requireUpdate ();
-      return;
-    }
+	return y + tank_off_y - tank_sag;
+}
 
-  // get bitmap for tank
-  if (player)
-  {
-      switch ( (int) player->tank_bitmap)
-        {
-        case CLASSIC_TANK:
-          use_tank_bitmap = 8;
-          use_turret_bitmap = 1;
-          turret_x = x;
-          turret_y = y + (TANKHEIGHT / 2);
-          break;
-        case BIGGREY_TANK:
-          use_tank_bitmap = 9;
-          use_turret_bitmap = 2;
-          turret_y = y;
-          turret_x = x;
-          break;
-        case T34_TANK:
-          use_tank_bitmap = 10;
-          use_turret_bitmap = 3;
-          turret_y = y;
-          turret_x = x;
-          break;
-        case HEAVY_TANK:
-          use_tank_bitmap = 11;
-          use_turret_bitmap = 4;
-          turret_y = y;
-          turret_x = x;
-          break;
-        case FUTURE_TANK:
-          use_tank_bitmap = 12;
-          use_turret_bitmap = 5;
-          turret_y = y;
-          turret_x = x;
-          break;
-        case UFO_TANK:
-          use_tank_bitmap = 13;
-          use_turret_bitmap = 6;
-          turret_y = y;
-          turret_x = x;
-          break;
-        case SPIDER_TANK:
-          use_tank_bitmap = 14;
-          use_turret_bitmap = 7;
-          turret_y = y;
-          turret_x = x;
-          break;
-         case BIGFOOT_TANK:
-          use_tank_bitmap = 15;
-          use_turret_bitmap = 8;
-          turret_y = y;
-          turret_x = x;
-          break;
-        case MINI_TANK:
-          use_tank_bitmap = 16;
-          use_turret_bitmap = 9;
-          turret_y = y;
-          turret_x = x;
-          break;
-        default:
-          use_tank_bitmap = 0;
-          use_turret_bitmap = 0;
-          turret_x = x;
-          turret_y = y;
-          break;
-        }
-    }
 
-  rectfill (dest, (int) x - (TANKWIDTH - 1),
-            (int) (y + TANKHEIGHT) - 2,
-            (int) (x + TANKWIDTH) - 2,
-            (int) (y + TANKHEIGHT),
-            this->player->color);
-  // draw_sprite (dest, (BITMAP *) _global->gfxData.T[use_tank_bitmap].dat, (int) x - TANKWIDTH, (int) y);
+/// @return The calculated tank diameter
+double TANK::getDiameter()
+{
+	return tank_dia;
+}
 
-/*
-  Drawing shields this way seems to cause a crash on multi-cpu
-  systems. Taking this out and creating new shield
-  drawing routine below. -- Jesse
-
-  if (sh > 0)
-    {
-      int phaseValue = 1;
-
-      drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0);
-      _env->current_drawing_mode = DRAW_MODE_TRANS;
-      set_trans_blender (0, 0, 0, (int)50);
-      // avoid sub-index
-      if ( shPhase < 0 )
-        shPhase = 0;
-      if (sht >= ITEM_LGT_SHIELD && sht <= ITEM_HVY_SHIELD)
-        {
-          phaseValue = (int)(_global->slope[(int)shPhase][0] * 3);
-        }
-      else if (sht >= ITEM_LGT_REPULSOR_SHIELD && sht <= ITEM_HVY_REPULSOR_SHIELD)
-        {
-          phaseValue = (int)(shPhase / 360 * 6);
-        }
-
-      ellipsefill (dest, (int) x, (int) y, TANKWIDTH + 6 + phaseValue, TANKHEIGHT - 1, shieldColor);
-      set_trans_blender (0, 0, 0, (int)
-                         (50 + (((float) sh / (float) (item[sht]).vals[SHIELD_ENERGY]) * (float) 150)));
-      for (int thicknessCount = 0; thicknessCount < shieldThickness; thicknessCount++)
-        {
-          ellipse (dest, (int) x - (shieldThickness / 2) + thicknessCount, (int) y,
-                   TANKWIDTH + 6 + phaseValue, TANKHEIGHT - 1, shieldColor);
-        }
-      drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);
-      _env->current_drawing_mode = DRAW_MODE_SOLID;
-    }   // end of drawing shield
-*/
 
-  if (sh > 0)
-  {
-     int thickness = 2;
-     int counter;
-     int wobble = (int) shPhase;
-
-     if ( (sht == ITEM_LGT_SHIELD) || (sht == ITEM_LGT_REPULSOR_SHIELD) )
-       thickness = 1;
-     else if ( (sht == ITEM_HVY_REPULSOR_SHIELD) || (sht == ITEM_HVY_SHIELD) )
-       thickness = 3;
-
-     if (! shieldColor)        // client may not have set colour
-     {
-         shieldColor = makecol ((int)item[sht].vals[SHIELD_RED],
-                             (int)item[sht].vals[SHIELD_GREEN],
-                             (int)item[sht].vals[SHIELD_BLUE]);
-     }
-     for (counter = 0; counter < thickness; counter++)
-         circle(dest, (int) x, (int) y, 
-                TANKHEIGHT + counter + wobble, shieldColor);
-  }
-
-  draw_sprite (dest, (BITMAP *) _global->tank[use_tank_bitmap], (int) x - TANKWIDTH, (int) y);
-  turretAngle = (int) ((float) (90 - a) * ((float) 256 / (float) 360));
-  rotate_sprite (dest, (BITMAP *) _global->tankgun[use_turret_bitmap],
-                 (int) turret_x - GUNLENGTH, (int) turret_y - (GUNLENGTH - 2), itofix (turretAngle));
-
-
-  // when using rockets, show flame
-  if (yv < 0)
-  {
-      rectfill(dest, (int) x - TANKWIDTH, (int) y + TANKHEIGHT, x + TANKWIDTH, y + TANKHEIGHT + 10, makecol(250, 150, 0) );
-  }
-
-  if (sh > 0)
-    setUpdateArea ((int)x - TANKWIDTH - 15, (int)y - TANKHEIGHT,
-                   ((TANKWIDTH + 15) * 2) + 1, (TANKHEIGHT * 2) + 20);
-  else
-    setUpdateArea ((int)x - GUNLENGTH - 1, (int)y - GUNLENGTH - 1,
-                   (GUNLENGTH * 2) + 2, TANKHEIGHT + GUNLENGTH + 20);
-  if (para)
-    {
-      draw_sprite (dest, (BITMAP *) _global->tank[para],
-                   (int) (x - TANKWIDTH) - 3, (int) y - 25);
-      addUpdateArea ((int)(x - TANKWIDTH) - 3, (int)y - 25, 35, 66);
-    }
+/// Sets @a top_x and @a top_y to the coordinates of the cannon tip
+void TANK::getGuntop(int32_t angle_, double &top_x, double &top_y)
+{
+	top_x = x + (env.slope[angle_][0] * turr_off_x);
+	top_y = y + (env.slope[angle_][1] * turr_off_y);
 
-  printHealth (healthOffset);
-  requireUpdate ();
+	// top_y must not be lower than tank y. (Lower means greater in value)
+	if (top_y > y)
+		top_y = y;
 }
 
-int TANK::get_heaviest_shield ()
+
+/// @return the current maxLife value
+int32_t TANK::getMaxLife()
 {
-  if (player->ni[ITEM_HVY_REPULSOR_SHIELD])
-    {
-      return ITEM_HVY_REPULSOR_SHIELD;
-    }
-  if (player->ni[ITEM_HVY_SHIELD])
-    {
-      return ITEM_HVY_SHIELD;
-    }
-  if (player->ni[ITEM_MED_REPULSOR_SHIELD])
-    {
-      return ITEM_MED_REPULSOR_SHIELD;
-    }
-  if (player->ni[ITEM_MED_SHIELD])
-    {
-      return ITEM_MED_SHIELD;
-    }
-  if (player->ni[ITEM_LGT_REPULSOR_SHIELD])
-    {
-      return ITEM_LGT_REPULSOR_SHIELD;
-    }
-  if (player->ni[ITEM_LGT_SHIELD])
-    {
-      return ITEM_LGT_SHIELD;
-    }
-  return ITEM_NO_SHIELD;
+	return maxLife;
 }
 
 
-
-
-void TANK::simActivateCurrentSelection ()
+/// @brief return true if a repulsor shield is up and running
+bool TANK::hasRepulsorActivated()
 {
-  char buf[16];
+	return (repulsion != 0);
+}
 
-  if (_global->turntype != TURN_SIMUL)
-    {
-      activateCurrentSelection();
-      if (fire_another_shot)
-        fire_another_shot--;
-    }
-  else
-    {
-      _env->stage = 1;
-    }
 
-  // allow naturals to happen again
-  _env->naturals_since_last_shot = 0;
+/// @brief return the number of pixels a tanks canon is buried
+/// If @a left and or @a right are given, they will receive the buried
+/// level on that side only.
+int32_t TANK::howBuried (int32_t* left, int32_t* right)
+{
+	int32_t result = 0;
+	int32_t old_x  = 0;
+	int32_t old_y  = 0;
+	int32_t cur_x  = 0;
+	int32_t cur_y  = 0;
+	double  angles_seen = 0.;
+
+	// Angles are from right (90) to left (270) counter clockwise
+	for (int32_t ta = 90; ta < 270; ++ta) {
+		cur_x = x + (env.slope[ta][0] * turr_off_x);
+		cur_y = y + (env.slope[ta][1] * turr_off_y);
+
+		if ( (cur_x != old_x) || (cur_y != old_y) ) {
+			if (PINK != getpixel(global.terrain, cur_x, cur_y))
+				++result;
+			old_x = cur_x;
+			old_y = cur_y;
+			angles_seen += 1.;
+		}
+
+		if (180 == ta) {
+			if (right) *right =  result;
+			if (left)  *left  = -result;
+		}
+	}
+
+	// Add full result to right to negate left half and count only right half
+	if (left) {
+		*left += result;
+		if (*left < 0)
+			*left = 0;
+	}
+
+	// The result must be adapted according how many *real* angles,
+	// meaning "missile starting points" are there.
+	// If boxed mode is on, the level is doubled, as the ceiling makes it
+	// very difficult to aim otherwise.
+	double angle_mod = 180. / angles_seen;
+
+	if (left)  *left  = ROUNDu(*left  * angle_mod / 2.);
+	if (right) *right = ROUNDu(*right * angle_mod / 2.);
+
+	result *= angle_mod * (env.isBoxed ? 1.25 : 1.);
+
+	return ROUNDu(result);
+}
 
-  // apply repairs
-  l += repair_rate;
-  if (l > maxLife)
-    l = maxLife;
-  sprintf (buf, "%d", l);
-  healthText->set_text(buf);
 
-  // avoid having key presses read in next turn
-  clear_keybuf();
+/// @return true if the tank is moving up or downwards (rocket / fall / glide)
+bool TANK::isFlying()
+{
+	return (yv < 0.) || (yv > 0.);
 }
 
 
+/// @return true if the tank is within the box defined by the given coordinates.
+bool TANK::isInBox(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
+{
+	double gun_x, gun_y;
+	getGuntop(a, gun_x, gun_y);
+	return ( (std::min(x1, x2) < std::max(x + tank_off_x, gun_x) )
+		  && (std::max(x1, x2) > std::min(x - tank_off_x, gun_x) )
+		  && (std::min(y1, y2) < (y + tank_off_y) )
+		  && (std::max(y1, y2) > std::min(y, gun_y) ) );
+}
 
 
-void TANK::activateCurrentSelection ()
+/** @return true if the tank is within the given ellipse.
+  * @param[in] ex Explosion x coordinate
+  * @param[in] ey Explosion y coordinate
+  * @param[in] rx Explosion x radius
+  * @param[in] ry Explosion y radius
+  * @param[out] in_rate_x The rate [0.;1.] of the tank x axis being in the ellipse.
+  * @param[out] in_rate_y The rate [0.;1.] of the tank y axis being in the ellipse.
+**/
+bool TANK::isInEllipse(double ex, double ey, double rx, double ry,
+					double &in_rate_x, double &in_rate_y)
 {
-  int z;
-
-  // avoid firing weapons on exit in Windows
-  if ( (_global->get_command() == GLOBAL_COMMAND_QUIT) ||
-       (_global->get_command() == GLOBAL_COMMAND_MENU) )
-    return;
-
-  // remove status from top bar at next redraw
-  if (_global->tank_status)
-      _global->tank_status[0] = 0;
-
-  _env->time_to_fall--;
-  if (_env->time_to_fall < 0)
-    // _env->time_to_fall = (rand() % MAX_GRAVITY_DELAY) + 1;
-    _env->time_to_fall = (rand() % (int)_env->landSlideDelay) + 1;
-
-  if (cw < WEAPONS)
-    {
-      player->changed_weapon = false;
-      if (cw)
-        player->nm[cw]--;
-
-      _env->stage = 1;
-      _env->am = weapon[cw].spread;
-      _env->realm = _env->am;
-
-      if (cw < BALLISTICS)
-        {
-          play_sample ((SAMPLE *) _global->sounds[weapon[cw].sound], _env->scaleVolume(255), 128, 1000, 0);
-          for (z = 0; z < _env->am; z++)
-            {
-              MISSILE *newmis;
-              double mxv,myv;
-              int ca;
-
-              ca = a + ((SPREAD * z) - (SPREAD * (_env->am - 1) / 2));
-              double dPower = (double)p;
-              if ((dPower < 200.0) && ((cw == RIOT_CHARGE) || (cw == RIOT_BLAST)))
-                dPower = 200.0;
-
-              mxv = _global->slope[ca][0] * dPower * (100.0 / _global->frames_per_second) / 100.0;
-              myv = _global->slope[ca][1] * dPower * (100.0 / _global->frames_per_second) / 100.0;
-
-              newmis = new MISSILE(_global, _env,
-                                   turret_x + (_global->slope[ca][0] * GUNLENGTH) /*- mxv*/,
-                                   turret_y + (_global->slope[ca][1] * GUNLENGTH) /*- myv*/,
-                                   mxv, myv, cw);
-              if (newmis)
-              {
-                newmis->physics = 0;
-                newmis->age = 0;
-                newmis->player = player;
-              }
-              else
-                  perror ( "tank.cc: Failed allocating memory for newmis in TANK::activateCurrentSelection");
-              // set up volley
-              if (! fire_another_shot)
-                {
-                    fire_another_shot = weapon[cw].delay * VOLLY_DELAY;
-                    if ( weapon[cw].delay )
-                    {
-                      shots_fired = shots_fired - (weapon[cw].delay - 1);
-                    }
-                }
-
-              if (player->ni[ITEM_DIMPLEP])
-                {
-                  player->ni[ITEM_DIMPLEP]--;
-                  newmis->drag *= item[ITEM_DIMPLEP].vals[0];
-                }
-              else if (player->ni[ITEM_SLICKP])
-                {
-                  player->ni[ITEM_SLICKP]--;
-                  newmis->drag *= item[ITEM_SLICKP].vals[0];
-                }
-            }
-        }
-      else          // BEAM weapon
-        {
-          play_sample ((SAMPLE *) _global->sounds[weapon[cw].sound], _env->scaleVolume(255), 128, 1000, 0);
-          for (z = 0; z < _env->am; z++)
-            {
-              BEAM *newbeam;
-              int ca;
-
-              ca = a + ((SPREAD * z) - (SPREAD * (_env->am - 1) / 2));
-
-              newbeam = new BEAM (_global, _env,
-                                  turret_x + (_global->slope[ca][0] * GUNLENGTH),
-                                  turret_y + (_global->slope[ca][1] * GUNLENGTH),
-                                  ca, cw);
-              if (newbeam)
-              {
-                 newbeam->physics = 0;
-                 newbeam->age = 0;
-                 newbeam->player = player;
-              }
-              else
-                  perror ( "tank.cc: Failed allocating memory for newbeam in TANK::activateCurrentSelection");
-            }
-
-        }
-    }
-  else           // activate an item
-    {
-      int itemNum = cw - WEAPONS;
-      if (itemNum < ITEM_VENGEANCE || itemNum > ITEM_FATAL_FURY)
-        player->ni[itemNum]--;
-      _env->stage = 1;
-      if (itemNum == ITEM_TELEPORT)
-        {
-          int teleXDest = (rand () % (_global->screenWidth - TANKWIDTH * 2)) + TANKWIDTH;
-          int teleYDest = (rand () % (_global->screenHeight - TANKHEIGHT * 2)) + TANKHEIGHT;
-          TELEPORT *teleport;
-          creditTo = player;
-          teleport = new TELEPORT (_global, _env, this, teleXDest, teleYDest, TANKHEIGHT * 4 + GUNLENGTH, 120);
-          if (!teleport)
-            {
-              perror ( "tank.cc: Failed allocating memory for teleport in TANK::activateCurrentSelection");
-              // exit (1);
-            }
-        }
-      else if (itemNum == ITEM_SWAPPER)
-        {
-          int random_tank_number;
-          TANK *other_tank;
-
-          // pick a random tank (not us)
-          random_tank_number = rand() % _global->numTanks;
-          other_tank = _env->order[random_tank_number];
-          while ( (! other_tank) || (other_tank == this) )
-            {
-              random_tank_number++;
-              if (random_tank_number > _global->maxNumTanks)
-                random_tank_number = 0;
-              other_tank = _env->order[random_tank_number];
-            }
-          creditTo = player;
-          // create a teleport ojbect for this tank
-          new TELEPORT (_global, _env, this, (int) other_tank->x, (int) other_tank->y, TANKHEIGHT * 4 + GUNLENGTH, 120);
-          // create a teleport object for the other tank
-          new TELEPORT (_global, _env, other_tank, (int) x, (int) y, TANKHEIGHT * 4 + GUNLENGTH, 120);
-
-        }
-      else if (itemNum == ITEM_MASS_TELEPORT)
-      {
-          int count;
-          int XDest, YDest;
-          TANK *current_tank;
-          
-          for (count = 0; count < _global->numPlayers; count++)
-          {
-             current_tank = _global->players[count]->tank;
-             if (current_tank)
-             {
-                XDest = (rand () % (_global->screenWidth - TANKWIDTH * 2)) + TANKWIDTH;
-                YDest = (rand () % (_global->screenHeight - TANKHEIGHT * 2)) + TANKHEIGHT;
-                creditTo = player;
-                new TELEPORT(_global, _env, current_tank, XDest, YDest, TANKHEIGHT * 4 + GUNLENGTH, 120);
-             }
-          }
-             
-      }
-
-      else if ( itemNum == ITEM_ROCKET )
-      {
-         yv = -10;
-         y -= 10;
-         if (a < 180)
-         {
-               xv += 0.3;
-         }
-         else if (a > 180)
-         {
-              xv -= 0.3;
-         }
-
-         applyPhysics();
-      }
-
-      else if ( itemNum == ITEM_FAN )
-        {
-          // play wind sound
-          play_sample ((SAMPLE *) _global->sounds[2], _env->scaleVolume(255), 128, 1000, 0);
-          if (a < 180)   // move wind to the right
-            _env->wind += (p / 20);
-          else     // wind to the left
-            _env->wind -= (p / 20);
-
-          // make sure wind is not too strong
-          if (_env->wind < (-_env->windstrength / 2) )
-            _env->wind = -_env->windstrength / 2;
-          else if (_env->wind > (_env->windstrength / 2) )
-            _env->wind = _env->windstrength / 2;
-
-          _env->lastwind = _env->wind;
-
-        }
-      else if ((itemNum >= ITEM_VENGEANCE) &&
-               (itemNum <= ITEM_FATAL_FURY))
-        {
-          creditTo = player;
-          damage = l + sh;
-        }
+	in_rate_x = 0.;
+	in_rate_y = 0.;
+
+	// The real gun tip height:
+	double gun_y_off = env.slope[a][1] * turr_off_y;
+
+	// But not below the tank:
+	if (gun_y_off > 0.)
+		gun_y_off = 0.;
+
+	// For the tank the real centre position and the radii must be known.
+	double ox       = static_cast<double>(tank_off_x);
+	double oy       = static_cast<double>(tank_off_y - gun_y_off) / 2.;
+	double tx       = x; // For consistencies sake...
+	double ty       = y + (static_cast<double>(tank_off_y + gun_y_off) / 2.);
+
+	double simpDist = FABSDISTANCE2(ex, ey, tx, ty);
+
+	// First handle a special case: The explosion takes place within the
+	// tank defined ellipse. This is a full direct hit.
+    if (simpDist <= (tank_dia / 2.)) {
+		in_rate_x = 1.;
+		in_rate_y = 1.;
+		DEBUG_LOG_PHY("Full Direct Hit", "=== T %d/%d (%dx%d) versus E %d/%d (%dx%d) ===",
+					  ROUND(tx), ROUND(ty), ROUND(ox), ROUND(oy),
+					  ROUND(ex), ROUND(ey), ROUND(rx), ROUND(ry))
+		DEBUG_LOG_PHY("Full Direct Hit", "Distance %5.2lf <= Dia half %5.2lf",
+		              simpDist, tank_dia / 2.)
+		return true;
     }
 
-  // if we are out of this type of weapon
-  // then switch to another
-  if (! player->nm[cw] )
-    {
-      cw = WEAPONS - 1;
-      while ( (cw > 0) && (! player->nm[cw] ) )
-        cw--;
-      player->changed_weapon = true;
-    }
 
-  shots_fired++;
-  player->time_left_to_fire = _global->max_fire_time;
+	/* The ideal solution would be to calculate the intersection of the two
+	 * ellipses which would enable us to tell the absolute correct value of how
+	 * much of the tank surface is covered by the explosion.
+	 *
+	 * Unfortunately this involves a 4th order equation to allow a numerical
+	 * solution. (I have found a very nice example written in JavaScript. It
+	 * uses several functions and has ~600 Lines. A bit much for a game, right?)
+	 *
+	 * The second best solution would be to determine the position on the rim of
+	 * each ellipse that is on a line between the two centres and then use their
+	 * distance. That is, if you blame this file and look up the commits, what I
+	 * tried first.
+	 * Unfortunately the exact location on the rim is almost as complex to
+	 * determine as the intersection. I did not see this in the beginning,
+	 * because I simply reused an algorithm of mine to check spheres in a 3D
+	 * space for collisions. But the hard truth is, that 3D spheres and 2D
+	 * ellipses are very different.
+	 *
+	 * So here is solution three. Still good enough for a game though. Just
+	 * check how much the x and y dimensions overlap and do a rough intersection
+	 * in two dimensions by reverse calculating the other axis. (This allows a
+	 * finer control of the damage for special shots like shaped charges,
+	 * though)
+	 */
+
+
+	// If shields are on, they must be taken into account
+	if (sh > 0.) {
+		ox = shld_rad_x;
+		oy = shld_rad_y;
+	}
+
+	// Determine start and end positions of explosion/tank intersections.
+	// The check is needed so hit/end are in the correct direction.
+	double hit_x = ex > tx ? std::min(ex + rx, tx + ox) : std::max(ex - rx, tx - ox);
+	double end_x = ex > tx ? std::max(ex - rx, tx - ox) : std::min(ex + rx, tx + ox);
+	double hit_y = ey > ty ? std::min(ey + ry, ty + oy) : std::max(ey - ry, ty - oy);
+	double end_y = ey > ty ? std::max(ey - ry, ty - oy) : std::min(ey + ry, ty + oy);
+	/* Check:
+	 *   ex = 100, tx = 150, rx = 40, ox = 20
+	 * hit_x = 100 > 150 ? ... : max(100 - 40, 150 - 20) = max(60, 130)  = 130
+	 * end_x = 100 > 150 ? ... : min(100 + 40, 150 + 20) = min(140, 170) = 140
+	 *   ex/tx swapped:
+	 * hit_x = 150 > 100 ? min(150 + 40, 100 + 20) = min(190, 120) = 120 : ...
+	 * end_x = 150 > 100 ? max(150 - 40, 100 - 20) = max(110, 80)  = 110 : ...
+	 *
+	 * This means start to end is always from explosion to tank.
+	 */
+
+
+	// Check whether the tank is in the explosion if both were boxes.
+	if ( (SIGN(ex - tx) != SIGN(hit_x - end_x))
+	  || (SIGN(ey - ty) != SIGN(hit_y - end_y)) )
+		return false;
+
+
+    // Handle another special case: The tank is fully within the explosion:
+    if ( (std::min(hit_x, end_x) <= (tx - ox))
+	  && (std::min(hit_y, end_y) <= (ty - oy))
+	  && (std::max(hit_x, end_x) >= (tx + ox))
+	  && (std::max(hit_y, end_y) >= (ty + oy)) ) {
+		in_rate_x = ((rx - std::abs(tx - ex) ) / rx) + 0.5;
+		in_rate_y = ((ry - std::abs(ty - ey) ) / ry) + 0.5;
+		if (in_rate_x > 1.0)
+			in_rate_x = 1.0;
+		if (in_rate_y > 1.0)
+			in_rate_y = 1.0;
+
+		// The full in_rate in "full washing over" must not be lower than 1/3
+		if (in_rate_x < 0.578)
+			in_rate_x = 0.578;
+		if (in_rate_y < 0.578)
+			in_rate_y = 0.578;
+
+		// Note: This value is taken, because 0.578 * 0.578 = 0.3341 (~1/3)
+
+		DEBUG_LOG_PHY("Full Indirect Hit", "=== T %d/%d (%dx%d) versus E %d/%d (%dx%d) ===",
+					  ROUND(tx), ROUND(ty), ROUND(ox), ROUND(oy),
+					  ROUND(ex), ROUND(ey), ROUND(rx), ROUND(ry))
+		DEBUG_LOG_PHY("Full Indirect Hit", "Left %d <= %d",
+		              ROUNDu(std::min(hit_x, end_x)), ROUNDu(tx - ox))
+		DEBUG_LOG_PHY("Full Indirect Hit", "Top %d <= %d",
+		              ROUNDu(std::min(hit_y, end_y)), ROUNDu(ty - oy))
+		DEBUG_LOG_PHY("Full Indirect Hit", "Right %d >= %d",
+		              ROUNDu(std::max(hit_x, end_x)), ROUNDu(tx + ox))
+		DEBUG_LOG_PHY("Full Indirect Hit", "Bottom %d >= %d",
+		              ROUNDu(std::max(hit_y, end_y)), ROUNDu(ty + oy))
+		DEBUG_LOG_PHY("Full Indirect Hit", "in_rate_x : %5.2lf", in_rate_x)
+		DEBUG_LOG_PHY("Full Indirect Hit", "in_rate_y : %5.2lf", in_rate_y)
+		DEBUG_LOG_PHY("Full Indirect Hit", "Distance %5.2lf > Dia half %5.2lf",
+		              simpDist, tank_dia / 2.)
+
+		return true;
+	}
+
+	// Get the middle of the x range and the explosions and tanks y rim there
+	double mid_x   = (hit_x + end_x) / 2.;
+	double e_off_y = std::abs(ry * std::sin(std::acos((ex - mid_x) / rx)));
+	double t_off_y = std::abs(oy * std::sin(std::acos((tx - mid_x) / ox)));
+	double rng_y   = std::min(ey + e_off_y, ty + t_off_y)
+	               - std::max(ey - e_off_y, ty - t_off_y);
+	/* Note:
+	 * If the explosion is within the tank range:
+	 *  ey+off < ty+off, ey-off > ty-off => rng = (ey+off) - (ey+off) => rng > 0
+	 * If the explosion is above the tank but reaches in:
+	 *  ey+off < ty+off, ey-off < ty-off => rng = (ey+off) - (ty-off) => rng > 0
+	 * If the explosion is below the tank but reaches is:
+	 *  ey+off > ty+off, ey-off > ty-off => rng = (ty+off) - (ey-off) => rng > 0
+	 * If the tank is fully within the explosion:
+	 *  ey+off > ty+off, ey-off < ty-off => rng = (ty+off) - (ty-off) => rng > 0
+	 * If the explosion is fully above the tank:
+	 *  ey+off < ty+off, ey-off < ty-off => rng = (ey+off) - (ty+off) => rng < 0
+	 * If the explosion is fully below the tank:
+	 *  ey+off > ty+off, ey-off > ty-off => rng = (ty+off) - (ey-off) => rng < 0
+	 */
+
+
+	// Opt out if the explosions rim y does not fit the tanks rim y
+	if ( rng_y < .01 ) {
+		DEBUG_LOG_PHY("Opt Out Y", "=== T %d/%d (%dx%d) versus E %d/%d (%dx%d) ===",
+					  ROUND(tx), ROUND(ty), ROUND(ox), ROUND(oy),
+					  ROUND(ex), ROUND(ey), ROUND(rx), ROUND(ry))
+		DEBUG_LOG_PHY("Opt Out Y", "ey    - ty    (%d - %d = %d / %5.3lf)",
+						ROUND(ey), ROUND(ty), ROUND(ey - ty), rng_y)
+		DEBUG_LOG_PHY("Opt Out Y", "Exp_y (%d to %d = %d)",
+						ROUND(ey - e_off_y), ROUND(ey + e_off_y), ROUND(2 * e_off_y))
+		DEBUG_LOG_PHY("Opt Out Y", "Tnk_y (%d to %d = %d)",
+						ROUND(ty - t_off_y), ROUND(ty + t_off_y), ROUND(2 * t_off_y))
+		return false;
+	}
+
+	// Get the middle of the y range and the explosions x rim there
+	double mid_y = (hit_y + end_y) / 2.;
+	double e_off_x = std::abs(rx * std::cos(std::asin((ey - mid_y) / ry)));
+	double t_off_x = std::abs(ox * std::cos(std::asin((ty - mid_y) / oy)));
+	double rng_x   = std::min(ex + e_off_x, tx + t_off_x)
+	               - std::max(ex - e_off_x, tx - t_off_x);
+
+	// Opt out if the explosions rim x does not fit the tanks rim x
+	if ( rng_x < .01 ) {
+		DEBUG_LOG_PHY("Opt Out X", "=== T %d/%d (%dx%d) versus E %d/%d (%dx%d) ===",
+					  ROUND(tx), ROUND(ty), ROUND(ox), ROUND(oy),
+					  ROUND(ex), ROUND(ey), ROUND(rx), ROUND(ry))
+		DEBUG_LOG_PHY("Opt Out X", "ex    - tx    (%d - %d = %d / %5.3lf)",
+						ROUND(ex), ROUND(tx), ROUND(ex - tx), rng_x)
+		DEBUG_LOG_PHY("Opt Out X", "Exp_x (%d to %d = %d)",
+						ROUND(ex - e_off_x), ROUND(ex + e_off_x), ROUND(2 * e_off_x))
+		DEBUG_LOG_PHY("Opt Out X", "Tnk_x (%d to %d = %d)",
+						ROUND(tx - t_off_x), ROUND(tx + t_off_x), ROUND(2 * t_off_x))
+		return false;
+	}
+
+
+	// Being here means that both ranges lie within the tanks ellipse.
+	in_rate_x = rng_x / ( ox * 2. );
+	in_rate_y = rng_y / ( oy * 2. );
+
+	assert ( (in_rate_x > 0.) && (in_rate_y > 0.) && "RANGE ERROR");
+
+	DEBUG_LOG_PHY("Overlapping", "=== T %d/%d (%dx%d) versus E %d/%d (%dx%d) ===",
+				  ROUND(tx), ROUND(ty), ROUND(ox), ROUND(oy),
+				  ROUND(ex), ROUND(ey), ROUND(rx), ROUND(ry))
+	DEBUG_LOG_PHY("Overlapping", "Hit: %4d/%4d ; End: %4d/%4d",
+					ROUNDu(std::min(hit_x, end_x)), ROUNDu(std::min(hit_y, end_y)),
+					ROUNDu(std::max(hit_x, end_x)), ROUNDu(std::max(hit_y, end_y)) )
+	DEBUG_LOG_PHY("Overlapping", "Exp: %4d/%4d ; Tnk %4d/%4d ; Rng %4d/%4d",
+					ROUNDu(e_off_x), ROUNDu(e_off_y),
+					ROUNDu(t_off_y), ROUNDu(t_off_y),
+					ROUNDu(rng_x), ROUNDu(rng_y) )
+	DEBUG_LOG_PHY("Overlapping", "in_rate_x  : %5.2lf", in_rate_x)
+	DEBUG_LOG_PHY("Overlapping", "in_rate_y  : %5.2lf", in_rate_y)
+
+	// distances over 50% radius are taken to reduce the rates further
+	double dist_x_mod = ((rx - std::abs(tx - ex) ) / rx) + 0.5;
+	double dist_y_mod = ((ry - std::abs(ty - ey) ) / ry) + 0.5;
+
+	// Limit to 0.578 to 1.0 (See full indirect hit why 0.578 is used)
+	if (dist_x_mod > 1.0)
+		dist_x_mod = 1.0;
+	if (dist_x_mod < 0.578)
+		dist_x_mod = 0.578;
+	if (dist_y_mod > 1.0)
+		dist_y_mod = 1.0;
+	if (dist_y_mod < 0.578)
+		dist_y_mod = 0.578;
+
+	DEBUG_LOG_PHY("Overlapping", "dist_x_mod : %5.2lf", dist_x_mod)
+	DEBUG_LOG_PHY("Overlapping", "dist_y_mod : %5.2lf", dist_y_mod)
+
+	in_rate_x *= dist_x_mod;
+	in_rate_y *= dist_y_mod;
+
+	// Trim both rates to 1.0
+	if (in_rate_x > 1.0)
+		in_rate_x = 1.0;
+	if (in_rate_y > 1.0)
+		in_rate_y = 1.0;
+
+	DEBUG_LOG_PHY("Overlapping", "Final x    : %5.2lf", in_rate_x)
+	DEBUG_LOG_PHY("Overlapping", "Final y    : %5.2lf", in_rate_y)
+
+	return true;
+}
 
-  // if not performing a volly and the player is AI then randomly select next weapon
-  // else if ( ( player->type > HUMAN_PLAYER ) && (! fire_another_shot) )
-  //    cw = player->Select_Random_Weapon();
 
+/** @brief move the tank one unit
+  * This function tries to move the tank either left or right one
+  * unit.
+  *
+  * @param[in] direction The direction to move, DIR_RIGHT or DIR_LEFT.
+  *
+  * @return true if the tank was moved, false otherwise
+**/
+bool TANK::moveTank(int32_t direction)
+{
+	// Return false now if there is no fuel or the tank is currently flying
+	if ( (player->ni[ITEM_FUEL] < 1 ) || (yv < 0.) || (yv > 0.) )
+		return false;
+
+	// Safety: assert DIR_LEFT/RIGHT
+	assert ( ((DIR_LEFT == direction) || (DIR_RIGHT == direction))
+			&& "ERROR: Call moveTank with either DIR_LEFT or DIR_RIGHT!");
+	if ( (DIR_LEFT != direction) && (DIR_RIGHT != direction))
+		return false;
+
+	// Check whether the target pixel is beyond the border or occupied
+	int32_t next_x = x + direction;
+	int32_t min_y  = y + tank_off_y - tank_sag - 4 ;
+
+	if ( (next_x < 1)
+	  || (next_x >= env.screenWidth)
+	  || (env.landType == LAND_NONE) )
+		return false;
+
+	if (PINK != getpixel(global.terrain, next_x, min_y))
+		return false;
+
+	// move tank
+	x += direction;
+	player->ni[ITEM_FUEL]--;
+
+	// Allow the tank to move up a bit if necessary
+	y = min_y + 5;
+	if (y > env.screenHeight - 1)
+		y = env.screenHeight - 1;
+
+	while ( ( y > min_y ) && (PINK != getpixel(global.terrain, x, y)))
+		--y;
+	// Now fix y back to normal coordinate
+	y -= tank_off_y - tank_sag;
+
+	// But secure y
+	if (y > (env.screenHeight - tank_off_y))
+		y = env.screenHeight - tank_off_y;
+
+	return true;
 }
 
 
-void TANK::boost_up_shield ()
+void TANK::newRound (int32_t pos_x, int32_t pos_y)
 {
-  char buf[10];
-  int s = get_heaviest_shield ();
-
-  if ((s != ITEM_NO_SHIELD) && (player->ni[s] > 0))
-    {
-      player->ni[s]--;
-      sh = (int)item[s].vals[SHIELD_ENERGY];
-      repulsion = (int)item[s].vals[SHIELD_REPULSION];
-      shieldColor = makecol ((int)item[s].vals[SHIELD_RED],
-                             (int)item[s].vals[SHIELD_GREEN],
-                             (int)item[s].vals[SHIELD_BLUE]);
-      shieldThickness = (int)item[s].vals[SHIELD_THICKNESS];
-      sht = s;
-      ds = sht;
-      player->last_shield_used = s;
-    }
-  if (sh)
-    {
-      sprintf (buf, "%d", sh);
-      shieldText->set_text (buf);
-    }
-  else
-    {
-      shieldText->set_text (NULL);
-    }
+	// A new round without set player is futile.
+	assert(player && "ERROR: TANK::newRound called with nullptr player");
+	if (nullptr == player)
+		return;
+
+	static char buf[10] = { 0x0 };
+
+	// Reclaim shield if there is one left from end of last round
+	player->reclaimShield();
+
+	// Reset all values
+	cw          = 0;
+	damage      = 0.;
+	para        = 0;
+	creditTo    = nullptr;
+	p           = MAX_POWER / 2;
+	a           = (rand () % 150) + 105;
+	sh          = 0;
+	sht         = ITEM_NO_SHIELD;
+	repulsion   = 0;
+	delay_fall  = env.landSlideDelay * 100;
+
+	// Re-calculate max life
+	double tmpL = (player->ni[ITEM_ARMOUR]   * item[ITEM_ARMOUR].vals[0])
+	            + (player->ni[ITEM_PLASTEEL] * item[ITEM_PLASTEEL].vals[0]);
+	maxLife     = 100 + (tmpL > 0. ? static_cast<int32_t>(std::pow(tmpL, .6)) : 0);
+	l           = maxLife;
+
+	// (re)-init health text
+	snprintf (buf, 9, "%d", l);
+	healthText.set_text (buf);
+	healthText.set_color(player->color);
+
+	// Re-calculate repair rate
+	int32_t num_kits        = player->ni[ITEM_REPAIRKIT];
+	int32_t increase_amount = 5;
+	repair_rate             = 0;
+	while (num_kits-- > 0) {
+		repair_rate += increase_amount;
+		if (increase_amount > 1)
+			--increase_amount;
+	}
+
+	// (re-)init name text
+	if (env.nameAboveTank) {
+		nameText.set_text ( player->getName() );
+		nameText.set_color( player->color     );
+	}
+
+	fire_another_shot = 0;
+
+	// Set used bitmaps, determine offsets and place tank
+	x = pos_x;
+	y = pos_y;
+	use_tankbitmap   = -1;
+	use_turretbitmap = -1;
+	setBitmap();
 }
+
+
 void TANK::reactivate_shield ()
 {
-  if (!sh)  		//if no shield remains, try to reload
-    {
-      boost_up_shield ();
-    }
+	// if no shield remains, try to reload
+	if (sh > 0)
+		return;
+
+	static char buf[5] = { 0x0 };
+
+	sht = ITEM_NO_SHIELD;
+
+	// Try to set the most heavy shield there is available:
+	if (player->ni[ITEM_HVY_REPULSOR_SHIELD])
+		sht = ITEM_HVY_REPULSOR_SHIELD;
+	else if (player->ni[ITEM_HVY_SHIELD])
+		sht = ITEM_HVY_SHIELD;
+	else if (player->ni[ITEM_MED_REPULSOR_SHIELD])
+		sht = ITEM_MED_REPULSOR_SHIELD;
+	else if (player->ni[ITEM_MED_SHIELD])
+		sht = ITEM_MED_SHIELD;
+	else if (player->ni[ITEM_LGT_REPULSOR_SHIELD])
+		sht = ITEM_LGT_REPULSOR_SHIELD;
+	else if (player->ni[ITEM_LGT_SHIELD])
+		sht = ITEM_LGT_SHIELD;
+
+	if (ITEM_NO_SHIELD != sht) {
+		player->ni[sht]--;
+		sh              = item[sht].vals[SHIELD_ENERGY];
+		repulsion       = item[sht].vals[SHIELD_REPULSION];
+		shld_col_outer  = makecol (item[sht].vals[SHIELD_RED],
+		                           item[sht].vals[SHIELD_GREEN],
+		                           item[sht].vals[SHIELD_BLUE]);
+		shld_thickness = item[sht].vals[SHIELD_THICKNESS];
+
+		player->last_shield_used = sht;
+		shld_phase    = 0.; // Start neutral.
+		snprintf (buf, 4, "%d", sh);
+		shieldText.set_text (buf);
+		setTextPositions(true);
+	}
 }
 
-int TANK::howBuried ()
+
+bool TANK::repulse (double xpos, double ypos, double* xa, double* ya,
+                    ePhysType phys_type)
 {
-  int turrAngle;
-  int buryCount = 0;
+	// If there is no repulsion or the physics type is
+	// not sensitive to repulsion, return at once.
+	if ( !repulsion
+	  || (PT_FUNKY_FLOAT == phys_type)
+	  || (PT_NONE        == phys_type)
+	  || (PT_ROLLING     == phys_type) )
+		return false;
+
+	double xdist = xpos - x;
+	double ydist = ypos - (y - turr_off_y + shld_rad_y);
+
+	// Apply a minimum distance so no extreme catapult shots happen
+	if (std::abs(xdist) <  0.25) xdist =  0.25 * SIGNd(xdist);
+	if (std::abs(ydist) <  0.25) ydist =  0.25 * SIGNd(ydist);
+
+	// Unless this is a burying type that currently comes from below,
+	// repulsion is done upwards, assuming the projectile comes from above.
+	if ( ( (PT_DIGGING == phys_type) && (ydist < 0.) )
+	  || ( (PT_DIGGING != phys_type) && (ydist > 0.) ) )
+		ydist *= -1.0; // Missiles normally come from above, diggers from below.
+
+	double distance2 = (xdist * xdist) + (ydist * ydist);
+	double distance  = sqrt (distance2);
+
+	if (distance < (5. * std::sqrt(static_cast<double>(repulsion))) ) {
+		double rep_mod = PT_DIGGING    == phys_type ? 0.15
+		               : PT_DIRTBOUNCE == phys_type ? 0.66
+		               : PT_SMOKE      == phys_type ? 0.75
+		               : 1.;
+		*xa = (repulsion * (xdist / distance) / distance2) * rep_mod * 0.75;
+		*ya = (repulsion * (ydist / distance) / distance2) * rep_mod * 1.50;
+		return true;
+	}
+
+	return false;
+}
 
-  for (turrAngle = 90; turrAngle < 270; turrAngle++)
-    {
-      if (getpixel (_env->terrain, (int)(x + (_global->slope[turrAngle][0] * GUNLENGTH)), (int)(y + (_global->slope[turrAngle][1] * GUNLENGTH))) != PINK)
-        buryCount++;
-    }
 
-  return (buryCount);
-}
 
-int TANK::shootClearance (int targetAngle, int minimumClearance)
+/// @brief Resets flash_damage and applies damage if flash_damage
+/// is greater than half the FPS (meaning ~0.5 seconds) or the tank is dead.
+void TANK::resetFlashDamage()
 {
-  int clearance = 2;
-  int iXpos, iYpos;
-  do
-    {
-      iXpos	=	(int)(x + (_global->slope[targetAngle][0] * (GUNLENGTH + clearance)));
-      iYpos = (int)(y + (_global->slope[targetAngle][1] * (GUNLENGTH + clearance)));
-      if ((iYpos <= MENUHEIGHT) || (iXpos <= 1) || (iXpos >= (_global->screenWidth - 2)))
-        clearance = minimumClearance; // done it! There can't be dirt any more!
-      else
-        clearance++;
-    }
-  while ((clearance < minimumClearance) && (getpixel(_env->terrain, iXpos, iYpos) == PINK));
-
-  // If we are going for a particular minimumClearance (< screenWidth), it is important whether we hit a wall
-  if (minimumClearance < _global->screenWidth)
-    {
-      int iWall = _env->current_wallType;
-      if (	_global->bIsBoxed && (iYpos <= MENUHEIGHT)
-           &&((iWall == WALL_STEEL) || (iWall == WALL_WRAP)) )
-        clearance = -1; // Wall hit on ceiling
-      if (	((iXpos <= 1) || (iXpos >= (_global->screenWidth - 2)))
-           &&(iWall == WALL_STEEL)	)
-        clearance = -1; // Wall hit on sides
-    }
-
-  return (clearance >= minimumClearance?1:0);
+	if ( (flashdamage > (env.frames_per_second / 2)) || destroy ) {
+		flashdamage = 0;
+		if (ROUND(damage) > 0)
+			applyDamage();
+		requireUpdate();
+	}
 }
 
-int TANK::isSubClass (int classNum)
+
+void TANK::setBitmap()
 {
-  if (classNum == TANK_CLASS)
-    return (TRUE);
-  else
-    return (FALSE);
-  //return (PHYSICAL_OBJECT::isSubClass (classNum));
+	if (!player)
+		return;
+
+	bool had_offsets = ((use_tankbitmap > -1) && (use_turretbitmap > -1));
+
+	if (TT_NORMAL == player->tankbitmap) {
+		use_tankbitmap   = 0;
+		use_turretbitmap = 0;
+	} else {
+		use_tankbitmap   = player->tankbitmap + TO_TANK;
+		use_turretbitmap = player->tankbitmap + TO_TURRET;
+	}
+
+	// Set needed offsets
+	tank_off_x = ROUNDu( env.tank[use_tankbitmap]->w / 2);
+	tank_off_y =         env.tank[use_tankbitmap]->h;
+	tank_sag   = ROUNDu(static_cast<double>(tank_off_y) / 2.66);
+	turr_off_x = ROUNDu( env.tankgun[use_turretbitmap]->w / 2);
+	turr_off_y = ROUNDu(env.tankgun[use_turretbitmap]->h / 2) - 2;
+	shld_rad_x = tank_off_x + (turr_off_x / 2) + 1;
+	shld_rad_y = ((tank_off_y + turr_off_y) / 2) + 1;
+
+	tank_dia   = FABSDISTANCE2(2. * std::max(tank_off_x, turr_off_x),
+	                          tank_off_y + (std::min(turr_off_x, turr_off_y) / 2.),
+	                          0., 0.);
+
+	// If these are new offsets, the position of the tank is too low and must be fixed:
+	if (!had_offsets)
+		y -= (tank_off_y - tank_sag);
+
+	// Be sure the placement is sane:
+    assert( ((x - tank_off_x) > 2)                     && "Placement too far left");
+    assert( ((x + tank_off_x) < (env.screenWidth - 3)) && "Placement too far right");
+
+    // Without debug mode, this must be fixed:
+    if ( (x - tank_off_x) < 3)
+		x = tank_off_x + 3;
+	if ( (x + tank_off_x) > (env.screenWidth - 4) )
+		x = env.screenWidth - 4 - tank_off_x;
 }
 
 
-
-/*
-This function checks to see if there is a tank directly below this
-one. This is to determine if we landed on someone.
-The function returns TRUE if we landed on another tank and
-FALSE if we did not.
--- Jesse
-*/
-int TANK::tank_on_tank( GLOBALDATA *global )
+void TANK::setTextPositions(bool renew_colour)
 {
-  int found_tank = FALSE;
-  int player_count = 0;
-  int delta_x, delta_y;
-
-  while ( ( player_count < global->numPlayers ) && (! found_tank) )
-    {
-      // check to make sure this player is alive
-      if ( global->players[player_count]->tank )
-        {
-          // make sure this isn't our own tank
-          if ( ( global->players[player_count]->tank->x != x ) || (global->players[player_count]->tank->y != y ) )
-            {
-              // check to see if tanks are within TANK_WIDTH of each other's x
-              delta_x = (int) (x - global->players[player_count]->tank->x);
-              delta_y = (int) (y - global->players[player_count]->tank->y);
-
-              if ( ( abs(delta_x) <= TANKWIDTH ) && ( (delta_y < 0) && (delta_y >= -TANKHEIGHT) ) )
-                found_tank = TRUE;
-
-            }    // end of this is our own tank
-        }
-      player_count++;
-    }
-
-  return found_tank;
+	int32_t textpos = -12 - turr_off_x;
+
+	if (sh > 0) {
+		shieldText.set_pos (x, y + textpos);
+		textpos -= 14;
+		if (renew_colour)
+			shieldText.set_color(TURQUOISE);
+	} else
+		shieldText.set_pos(-1, -1);
+
+	healthText.set_pos (x, y + textpos);
+	textpos -= 14;
+	if (renew_colour)
+		healthText.set_color(player ? player->color : WHITE);
+
+	if (env.nameAboveTank) {
+		nameText.set_pos (x, y + textpos);
+		if (renew_colour)
+			shieldText.set_color(player ? player->color : WHITE);
+	}
 }
 
 
-
-/*
-This function figures out how many points a tank
-will repair itself each turn. This is based
-on the number of repair kits a player has.
-The amount is returned as an int.
--- Jesse
-*/
-int TANK::Get_Repair_Rate()
+bool TANK::shootClearance (int32_t targetAngle, int32_t minimumClearance,
+                           bool &crashed)
 {
-  int num_kits;
-  int repair_units = 0;
-  int increase_amount = 5;
-
-  num_kits = player->ni[ITEM_REPAIRKIT];
-  while (num_kits > 0)
-    {
-      repair_units += increase_amount;
-      if (increase_amount > 1)
-        increase_amount--;
-      num_kits--;
-    }
-
-  return repair_units;
+	int32_t clearance = 2;
+	double xmov = env.slope[targetAngle][0];
+	double ymov = env.slope[targetAngle][1];
+	double xpos = x + (xmov * (turr_off_x + clearance));
+	double ypos = y + (ymov * (turr_off_x + clearance));
+	bool   done = false;
+	crashed     = false;
+
+	while (!done) {
+		xpos += xmov;
+		ypos += ymov;
+
+		if ( (ypos <= MENUHEIGHT)
+		  || (xpos < 2) || (xpos > (env.screenWidth - 2) ) ) {
+			clearance = minimumClearance; // done it! There can't be dirt any more!
+			done      = true;
+		} else {
+			if (++clearance >= minimumClearance)
+				done = true;
+			else {
+				if (PINK != getpixel(global.terrain, xpos, ypos))
+					done = true;
+			} // End of having to check a pixel
+		} // End of being within screen bounds
+	} // End of checking the path
+
+	// If a minimum clearance lower than the screen width is sought,
+	// check whether this results in a wall/ceiling hit.
+	if ( (minimumClearance < env.screenWidth)
+	  && ( ( env.isBoxed && (ypos <= MENUHEIGHT)
+		  && ( (WALL_STEEL == env.current_wallType)
+			|| (WALL_WRAP  == env.current_wallType) ) )
+		|| ( (  WALL_STEEL == env.current_wallType)
+		  && ( (xpos < 2 ) || (xpos > (env.screenWidth - 3)) ) ) ) ) {
+		clearance = -1;
+		crashed   = true;
+	}
+
+	return (clearance >= minimumClearance);
 }
 
 
-
-/*
-This function tries to move the tank either
-left or right one unit. The direction is passed
-in.
-The function returns TRUE if the tank is moved and
-FALSE if something is in the way or the
-tank cannot be moved for some reason.
--- Jesse
-*/
-int TANK::Move_Tank(int direction)
+/// @brief this is used whenever a weapon really is triggered.
+/// In simultaneous play this does not actually mean it is fired.
+/// To fire another shot without the trigger action, call
+/// activateCurrentSelection().
+void TANK::simActivateCurrentSelection ()
 {
-  int pixel;
-  int destination_x;
-
-  // do we have fuel?
-  if ( player->ni[ITEM_FUEL] < 1 )
-    return FALSE;
-
-  // see where we want to go
-  if (direction == DIR_RIGHT)
-    destination_x = (int) (x + TANKWIDTH - 2);
-  else
-    destination_x = (int) (x - (TANKWIDTH + 1) );
-
-  if (_env->landType != LANDTYPE_NONE)
-    {
-      // check for something in the way
-      pixel = getpixel (_env->terrain, destination_x, (int) (y + (TANKHEIGHT / 2)) );
-      if (pixel != PINK)
-        return FALSE;
-    }
+	static char buf[6] = { 0x0 };
 
-  // move tank
-  if (direction == DIR_RIGHT)
-    x++;
-  else
-    x--;
+	if (env.turntype != TURN_SIMUL) {
+		activateCurrentSelection();
 
-  player->ni[ITEM_FUEL]--;
+		if (fire_another_shot)
+			fire_another_shot--;
+	}
 
-  return TRUE;
-}
+	// allow naturals to happen again
+	global.naturals_activated = 0;
 
-void TANK::setEnvironment(ENVIRONMENT *env)
-{
-  if (!_env || (_env != env))
-    {
-      _env = env;
-      _index = _env->addObject (this);
-    }
+	// apply repairs
+	l += repair_rate;
+	if (l > maxLife)
+		l = maxLife;
+
+	snprintf (buf, 5, "%d", l);
+	healthText.set_text(buf);
+
+	// avoid having key presses read in next turn
+	clear_keybuf();
 }
 
 
-/*
-Give credit to the tank who killed us.
-*/
-void TANK::Give_Credit(GLOBALDATA *global)
+/** @brief return true if this tank landed on another tank
+  *
+  * This function checks to see if there is a tank directly below this
+  * one. This is to determine if we landed on someone.
+  *
+  * @return true if the tank landed on another one, false otherwise
+**/
+bool TANK::tank_on_tank()
 {
-     if (creditTo)
-     {
-        // we shot ourselves
-        if (creditTo == player)
-           creditTo->money -= (int) global->scoreUnitSelfDestroy;
-        else   // we were killed by someone else
-           creditTo->money += (int) global->scoreUnitDestroyBonus;
-
-        // avoid over-flow
-        if (creditTo->money < 0)
-            creditTo->money = 0;
-        creditTo = NULL;
-     }
+	TANK* lt         = nullptr;
+	bool  found_tank = false;
+
+	global.getHeadOfClass(CLASS_TANK, &lt);
+	while (lt && !found_tank) {
+		if ( (lt != this)
+		  && (std::abs(lt->x - x) < tank_off_x)
+		  && (lt->y > y)
+		  && ((lt->y - y) < tank_off_y) )
+			found_tank = true;
+		else
+			lt->getNext(&lt);
+	}
+
+	return found_tank;
 }
-
diff --git a/src/tank.h b/src/tank.h
index cd27602..7705662 100644
--- a/src/tank.h
+++ b/src/tank.h
@@ -22,79 +22,128 @@
 
 
 #include "physobj.h"
-
-#define	TELETIME	120
+#include "floattext.h"
 
 #define DIR_RIGHT 1
-#define DIR_LEFT 2
+#define DIR_LEFT -1
 
 #define VIOLENT_CHANCE 6
 
 
 class PLAYER;
 class EXPLOSION;
-class FLOATTEXT;
 class TANK: public PHYSICAL_OBJECT
-  {
-  protected:
-    int	_targetX,_targetY;
-    int	_prevTargetX,_prevTargetY;
-    TANK	*_target;
-
-  public:
-    int	bestPower, bestAngle;
-    int	smallestOvershoot;
-    int 	t, a, p, cw, l, sh, sht, fs, ds, para, pen;
-    int	maxLife;	// amount awarded at beginning of round
-    double	damage;
-    int     repair_rate;
-    int	repulsion;
-    int	shieldColor, shieldThickness;
-    int	flashdamage, hit;
-    int	timer;
-    int	teleTimer;
-    int	teleXDest, teleYDest;
-    double	shPhase, delta_phase;
-    PLAYER	*creditTo;
-    FLOATTEXT	*healthText, *shieldText;//, *damageText;
-    FLOATTEXT       *nameText;
-    int use_tank_bitmap, use_turret_bitmap;
-    double turret_x, turret_y;
-    int delay_fall;         // time the tank will hover
-    int fire_another_shot;
-    int shots_fired;
-
-    ~TANK ();
-    TANK (GLOBALDATA *global, ENVIRONMENT *env);
-    void	initialise ();
-    void Destroy();
-    int	get_heaviest_shield ();
-    void	boost_up_shield ();
-    void	reactivate_shield ();
-    void	drawTank (BITMAP *dest, int healthOffset);
-    void	printHealth (int offset);
-    void	update ();
-    void	requireUpdate ();
-    void	explode ();
-    void    simActivateCurrentSelection();
-    void	activateCurrentSelection ();
-    int	isSubClass (int classNum);
-    inline virtual int	getClass ()
-    {
-      return (TANK_CLASS);
-    }
-    virtual void setEnvironment(ENVIRONMENT *env);
-    void	applyDamage ();
-    int	applyPhysics ();
-    void	repulse (double xpos, double ypos, double *xaccel, double *yaccel, int aWeaponType);
-    int	howBuried ();
-    int	shootClearance (int targetAngle, int minimumClearance = MAX_OVERSHOOT);
-    void	newRound ();
-    void	framelyAccounting ();
-    int     tank_on_tank(GLOBALDATA *gd); //is this tank on top of another?
-    int     Get_Repair_Rate();    // how many units a tank will repair itself per round
-    int     Move_Tank(int direction);
-    void    Give_Credit(GLOBALDATA *global);  // give credit to killer
-  };
+{
+
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	explicit TANK ();
+	~TANK ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void    activate();
+	void    activateCurrentSelection ();
+	void    addDamage(PLAYER* damageFrom, double damage_);
+	void    applyDamage();
+	void    applyPhysics();
+	void    check_weapon();
+	void    deactivate();
+	void    draw ();
+	void    explode(bool allow_vengeance);
+	int32_t getBottom();
+	double  getDiameter();
+	void    getGuntop(int32_t angle_, double &top_x, double &top_y);
+	int32_t getMaxLife();
+	bool    hasRepulsorActivated();
+	int32_t howBuried(int32_t* left, int32_t* right);
+	bool    isFlying();
+	bool    isInBox(int32_t x1, int32_t y1, int32_t x2, int32_t y2);
+	bool    isInEllipse(double ex, double ey, double rx, double ry,
+	                    double &in_rate_x, double &in_rate_y);
+	bool    moveTank(int32_t direction);
+	void    newRound (int32_t pos_x, int32_t pos_y);
+	void    reactivate_shield ();
+	bool    repulse (double xpos, double ypos, double* xa, double* ya,
+	                 ePhysType phys_type);
+	void    resetFlashDamage();
+	bool    shootClearance (int32_t targetAngle, int32_t minimumClearance,
+	                        bool &crashed);
+	void    simActivateCurrentSelection();
+
+	eClasses getClass() { return CLASS_TANK; }
+
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+	int32_t   a                 = 90;      // [a]ngle
+	int32_t   cw                = SML_MIS; // [c]urrent [w]eapon
+	int32_t   fire_another_shot = 0;
+	FLOATTEXT healthText;
+	int32_t   l                 = 100;     // [l]ive
+	FLOATTEXT nameText;
+	int32_t   p                 = MAX_POWER / 2; // [p]ower
+	int32_t   sh                = 0;       // [sh]ield
+	FLOATTEXT shieldText;
+	int32_t   sht               = 0;       // [sh]ield [t]ype
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void    setBitmap();
+	void    setTextPositions(bool renew_colour);
+	bool    tank_on_tank(); // is this tank on top of another?
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	PLAYER*       creditTo         = nullptr;
+	double        damage           = 0.;
+	CSpinLock     damage_lock;
+	int32_t       delay_fall       = env.landSlideDelay * 100; // time the tank will hover
+	int32_t       flashdamage      = 0;
+	bool          isTeleported     = false; // Set to true if a teleport occurs to award falling damage.
+	int32_t       maxLife          = 100; // amount awarded at beginning of round
+	bool          newDamager       = false;
+	int32_t       para             = 0;
+	int32_t       repair_rate      = 0;
+	int32_t       repulsion        = 0;
+	int32_t shld_col_inner   = BLACK;
+	int32_t shld_col_outer   = BLACK;
+	double        shld_delta       = 360.; // divided by FPS in ctor
+	double        shld_phase       = 0.; // Neutral
+	int32_t       shld_rad_x       = 0; // Determined by the used bitmap
+	int32_t       shld_rad_y       = 0; // Determined by the used bitmap
+	int32_t       shld_thickness   = 0;
+	double        tank_dia         = 1.; // Tank diameter, determined by the used bitmap
+	int32_t       tank_off_x       = 0; // Determined by the used bitmap
+	int32_t       tank_off_y       = 0; // Determined by the used bitmap
+	int32_t       tank_sag         = 0; // Determined by the used bitmap
+	int32_t       turr_off_x       = 0; // Determined by the used bitmap
+	int32_t       turr_off_y       = 0; // Determined by the used bitmap
+	int32_t       use_tankbitmap   = -1;
+	int32_t       use_turretbitmap = -1;
+};
+
+#define HAS_TANK 1
 
 #endif
diff --git a/src/team.cpp b/src/team.cpp
deleted file mode 100644
index 598ba62..0000000
--- a/src/team.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "tank.h"
-#include "team.h"
-#include "player.h"
-
-// check to see if a team has won.
-int Team_Won(GLOBALDATA *global)
-{
-  bool all_jedi = true;
-  bool all_sith = true;
-  int current_player = 0;
-  int my_team = 1;
-  int player_count = 0;
-
-  while ((all_jedi || all_sith) && ( current_player < global->numPlayers) )
-    {
-      if ( (global->players[current_player]->tank) &&
-           (global->players[current_player]->tank->l) )
-        {
-          my_team = (int)global->players[current_player]->team;
-          if ( (my_team == TEAM_JEDI) || (my_team == TEAM_NEUTRAL) )
-            all_sith = false;
-
-          if ( (my_team == TEAM_SITH) || (my_team == TEAM_NEUTRAL) )
-            all_jedi = false;
-          player_count++;
-        }
-      current_player++;
-    }
-
-  if (! player_count) return NO_WIN;
-  if (all_jedi) return JEDI_WIN;
-  else if (all_sith) return SITH_WIN;
-  else return NO_WIN;
-}
-
diff --git a/src/team.h b/src/team.h
deleted file mode 100644
index 1a99671..0000000
--- a/src/team.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-This file contains team related functions that
-do not fit anywhere else.
-*/
-
-#ifndef TEAM_HEADER_FILE__
-#define TEAM_HEADER_FILE__
-
-#include "globaldata.h"
-
-#define NO_WIN 0
-#define JEDI_WIN 100
-#define SITH_WIN 200
-
-// This function checks to see if one
-// team (Jedi or Sith) has won. Neutral
-// is not considered a team.
-// The function return the following:
-// 0 - no team won
-// JEDI_WON - Jedi team won
-// SITH_WON - Sith team won
-
-int Team_Won(GLOBALDATA *global);
-
-#endif
-
diff --git a/src/teleport.cpp b/src/teleport.cpp
index 6e8aed6..a9c3bc8 100644
--- a/src/teleport.cpp
+++ b/src/teleport.cpp
@@ -21,191 +21,205 @@
 #include "environment.h"
 #include "globaldata.h"
 #include "teleport.h"
+#include "tank.h"
+#include "sound.h"
+
+#ifdef NETWORK
+#  include "player.h"
+#endif
 
 TELEPORT::~TELEPORT ()
 {
-  requireUpdate();
-  update();
-  _env->make_bgupdate (_current.x, _current.y, _current.w, _current.h);
-  _env->make_bgupdate (_old.x, _old.y, _old.w, _old.h);
-  _env->removeObject (this);
-  _env        = NULL;
-  _global     = NULL;
-  object      = NULL;
-  remote      = NULL;
+	requireUpdate();
+	update();
+	if (dim_cur.w > 0)
+		global.make_bgupdate (dim_cur.x, dim_cur.y, dim_cur.w, dim_cur.h);
+	if (dim_old.w > 0)
+		global.make_bgupdate (dim_old.x, dim_old.y, dim_old.w, dim_old.h);
+
+	if (remote) {
+		remote->destroy = true;
+		remote->remote  = nullptr;
+	}
+
+	object = nullptr;
+	remote = nullptr;
+
+	// Take out of the chain:
+	global.removeObject(this);
 }
 
-TELEPORT::TELEPORT (GLOBALDATA *global, ENVIRONMENT *env, VIRTUAL_OBJECT *targetObj, int destinationX, int destinationY,
-                    int objRadius, int duration):VIRTUAL_OBJECT(),clock(duration),startClock(duration),peaked(0),
-                                                                  object(NULL),remote(NULL)
+TELEPORT::TELEPORT (VIRTUAL_OBJECT *targetObj,
+                    int32_t destinationX, int32_t destinationY,
+                    int32_t objRadius, int32_t duration, int32_t type):
+	VIRTUAL_OBJECT(),
+	clock(duration),
+	object(targetObj),
+	radius(objRadius),
+	startClock(duration)
 {
-  setEnvironment (env);
-  player = NULL;
-  _align = LEFT;
-  _global = global;
-  object = targetObj;
-  radius = objRadius;
-  remote = new TELEPORT (global, env, this, destinationX, destinationY);
-  if (!remote)
-    {
-      perror ( "teleport.cc: Failed allocating memory for remote in TELEPORT::TELEPORT");
-    }
-  play_sample ((SAMPLE *) _global->sounds[item[ITEM_TELEPORT].sound], _env->scaleVolume(255), 128, 1000, 0);
-
-  #ifdef NETWORK
-  // this seems to be the teleport we usually use
-  char buffer[64];
-  int player_index = 0;
-  bool found = false;
-  TANK *the_tank = (TANK*) targetObj;
-  // match the player with the tank
-  while ( (player_index < global->numPlayers) && (! found) )
-  {
-      if ( ( global->players[player_index]->tank ) && (global->players[player_index]->tank == the_tank) )
-        found = true;
-     else
-        player_index++;
-  }
-  if (found)
-  {
-     sprintf(buffer, "TELEPORT %d %d %d", player_index, destinationX, destinationY);
-     global->Send_To_Clients(buffer);
-  }
-  #endif
+
+	if (object) {
+		x = object->x;
+		y = object->y;
+	}
+
+	// Ensure the destination is not occupied by another tank:
+	bool need_check = (type != ITEM_SWAPPER);
+	while (need_check) {
+		TANK* lt = nullptr;
+		need_check = false;
+
+		global.getHeadOfClass(CLASS_TANK, &lt);
+		while ( lt ) {
+			if ( (std::abs(lt->x - destinationX) < objRadius)
+			  && (lt->y > destinationY)
+			  && ((lt->y - destinationY) < objRadius) ) {
+				need_check = true;
+
+				// Maybe move left
+				if ( ( (destinationX >  (objRadius * 2))
+				    && (destinationX <= lt->x) )
+				  || (destinationX >= (env.screenWidth - (objRadius * 2))) )
+					destinationX -= std::abs(lt->x - destinationX);
+				// Or move right
+				else if (destinationX < (env.screenWidth - (objRadius * 2)))
+					destinationX += std::abs(lt->x - destinationX);
+
+				// Maybe move up
+				if ( ( (destinationY >  (MENUHEIGHT + (objRadius * 2)) )
+				    && (destinationY <= lt->y) )
+				  || (destinationY >= (env.screenHeight - (objRadius * 2))) )
+					destinationY -= std::abs(lt->y - destinationY);
+				// Or move down
+				else if (destinationY < (env.screenHeight - (objRadius * 2)))
+					destinationY += std::abs(lt->y - destinationY);
+			}
+
+
+			lt->getNext(&lt);
+		}
+	} // end of needing to check the destination
+
+	try {
+		remote = new TELEPORT (this, destinationX, destinationY);
+	} catch(std::bad_alloc &e) {
+		std::cerr << "Error creating TELEPORT: " << e.what() << std::endl;
+		perror ( "teleport.cc: Failed allocating memory for remote in TELEPORT::TELEPORT");
+	}
+
+	play_fire_sound(ITEM_TELEPORT + WEAPONS, x, 255, 1000);
+
+#ifdef NETWORK
+	// this seems to be the teleport we usually use
+	int   playerindex = 0;
+	bool  found       = false;
+	TANK* the_tank    = static_cast<TANK*>(targetObj);
+
+	// match the player with the tank
+	while ( (playerindex < env.numGamePlayers) && (! found) ) {
+		if ( (env.players[playerindex]->tank)
+		  && (env.players[playerindex]->tank == the_tank) )
+			found = true;
+		else
+			++playerindex;
+	}
+
+	if (found) {
+		char  buffer[64]  = { 0x0 };
+		snprintf(buffer, 63, "TELEPORT %d %d %d",
+				playerindex, destinationX, destinationY);
+		env.sendToClients(buffer);
+	}
+#endif // NETWORK
+
+	// Add to the chain:
+	global.addObject(this);
 }
 
 
 
 
-TELEPORT::TELEPORT (GLOBALDATA *global, ENVIRONMENT *env, TELEPORT *remoteEnd, int destX, int destY):VIRTUAL_OBJECT(),
-                    clock(remoteEnd->startClock),startClock(remoteEnd->startClock),peaked(0)
+TELEPORT::TELEPORT (TELEPORT *remoteEnd, int32_t destX, int32_t destY) :
+	VIRTUAL_OBJECT(),
+	remote(remoteEnd)
 {
-  remote = remoteEnd;
-  setEnvironment (env);
-  player = NULL;
-  _align = LEFT;
-  _global = global;
-  object = NULL;
-  x = destX;
-  y = destY;
-  radius = remote->radius;
+	this->x = destX;
+	this->y = destY;
+	if (remote) {
+		clock = remoteEnd->startClock;
+		radius = remoteEnd->radius;
+		startClock = remoteEnd->startClock;
+	}
+
+	// Add to the chain:
+	global.addObject(this);
 }
 
-void TELEPORT::initialise ()
+void TELEPORT::applyPhysics ()
 {
-  VIRTUAL_OBJECT::initialise ();
-  peaked = 0;
-  clock = startClock;
+	if (object) {
+		if (!clock) {
+			object->x = remote->x;
+			object->y = remote->y;
+			remote->object = object;
+			object = nullptr;
+			remote->clock--;
+		}
+	} else
+		clock = remote->clock;
+
+	if (clock-- < -startClock / 2)
+		destroy = true;
 }
 
-int TELEPORT::applyPhysics ()
+
+void TELEPORT::draw ()
 {
-  if (object)
-    {
-      x = object->x;
-      y = object->y;
-    }
-  else
-    {
-      clock = remote->clock;
-    }
-  if (clock < 1)
-    {
-      if (clock == 0)
-        {
-          if (object)
-            {
-              object->x = remote->x;
-              object->y = remote->y;
-              remote->object = object;
-              object = NULL;
-              remote->clock--;
-            }
-        }
-      if (clock < -startClock / 2)
-        destroy = TRUE;
-    }
-  clock--;
-  return (0);
-}
+	if (!remote)
+		return;
 
+	double  pClock     = clock;
+	int32_t blobSize   = 8;
+	int32_t pRadius    = radius;
+	int32_t maxblobs   = 1;
 
-// new telport version
-void TELEPORT::draw (BITMAP *dest)
-{
-  BITMAP *tempBitmap;
-  double pClock = clock;
-  int blobSize, pRadius, maxblobs;
-
-  if (pClock < 1.0)
-    pClock = 1.0 + (1.0 - (pClock * 2.0));
-  blobSize = 8 - (int)round(8 / (startClock / pClock)) + 1;
-  pRadius  = radius - (int)round(radius / (startClock / pClock)) + 1;
-  maxblobs = 1 + (pRadius * 4);
-
-  tempBitmap = create_bitmap (radius * 2, radius * 2);
-  blit (dest, tempBitmap, remote->x - radius, remote->y - radius, 0, 0, radius * 2, radius * 2);
-
-  if (object)
-    remote->draw(dest);
-
-  drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-  set_trans_blender (0, 0, 0, 255 - (int)((pClock / startClock) * 255));
-  for (int count = (int)round(maxblobs + pClock); count > pClock; count--)
-    {
-      int xOff = (int)(perlin2DPoint (1.0, 200, 1278 + x + count * 100, pClock, 0.25, 6) * pRadius);
-      int yOff = (int)(perlin2DPoint (1.0, 200, 9734 + y + count * 100, pClock, 0.25, 6) * pRadius);
-      circlefill (dest, x + xOff, y + yOff, blobSize, getpixel (tempBitmap, pRadius + xOff, pRadius + yOff));
-    }
-  drawing_mode (DRAW_MODE_SOLID, NULL, 0, 0);
-  setUpdateArea (x - pRadius - blobSize, y - pRadius - blobSize, (pRadius + blobSize) * 2, (pRadius + blobSize) * 2);
-  requireUpdate ();
-
-  destroy_bitmap (tempBitmap);
-}
+	// When the teleporting finishes, the blobs enlarge and disperse
+	// using this then growing factor:
+	if (pClock < 1.0)
+		pClock = 1.0 + (1.0 - (pClock * 2.0));
 
+	int32_t transMod = 255 - (pClock / startClock * 255);
+	if (transMod > 255) transMod = 255;
+	else if (transMod < 0) transMod = 0;
 
-/*
- * Old version
-void TELEPORT::draw (BITMAP *dest)
-{
-  BITMAP *tempBitmap;
-  int blobSize = 8;
-  int pClock = clock;
-  if (pClock < 0)
-    {
-      pClock = 0;
-      blobSize = (startClock / 2) / -clock;
-    }
-
-  tempBitmap = create_bitmap (radius * 2, radius * 2);
-  blit (_env->db, tempBitmap, (int)remote->x - radius, (int)remote->y - radius, 0, 0, radius * 2, radius * 2);
-
-  if (object)
-    remote->draw (dest);
-
-  drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
-  set_trans_blender (0, 0, 0, 255 - (int)((pClock / startClock) * 255));
-  for (int count = (radius * radius * 4 / (8 * 8)); count > pClock; count--)
-    {
-      int xOff = (int)(perlin2DPoint (1.0, 200, 1278 + (int)x + count * 100, clock, 0.25, 6) * (radius));
-      int yOff = (int)(perlin2DPoint (1.0, 200, 9734 + (int)y + count * 100, clock, 0.25, 6) * (radius));
-      circlefill (dest, (int)x + xOff, (int)y + yOff, blobSize, getpixel (tempBitmap, radius + xOff, radius + yOff));
-    }
-  drawing_mode (DRAW_MODE_SOLID, NULL, 0, 0);
-  setUpdateArea ((int)x - radius - blobSize, (int)y - radius - blobSize, (radius + blobSize) * 2, (radius + blobSize) * 2);
-  requireUpdate ();
-
-  destroy_bitmap (tempBitmap);
-}
-*/
+	blobSize -= round(8 / (startClock / pClock)) + 1;
+	pRadius  -= round(radius / (startClock / pClock)) + 1;
+	maxblobs += pRadius * 4;
 
+	BITMAP* tempBitmap = create_bitmap (radius * 2, radius * 2);
+	blit (global.canvas, tempBitmap,
+		remote->x - radius, remote->y - radius, 0, 0,
+		radius * 2, radius * 2);
 
-int TELEPORT::isSubClass (int classNum)
-{
-  if (classNum == TELEPORT_CLASS)
-    return (TRUE);
-  else
-    return (FALSE);
-  //return (PHYSICAL_OBJECT::isSubClass (classNum));
+	if (object && remote)
+		remote->draw();
+
+	drawing_mode (DRAW_MODE_TRANS, NULL, 0, 0);
+	set_trans_blender (0, 0, 0, transMod);
+
+	for (int32_t i = round(maxblobs + pClock); i > pClock; --i) {
+		int32_t xOff = perlin2DPoint (1.0, 200, 1278 + x + (i * 100), pClock, 0.25, 6) * pRadius;
+		int32_t yOff = perlin2DPoint (1.0, 200, 9734 + y + (i * 100), pClock, 0.25, 6) * pRadius;
+		int32_t t_col = getpixel(tempBitmap, pRadius + xOff, pRadius + yOff);
+		circlefill (global.canvas, x + xOff, y + yOff, blobSize, t_col);
+	}
+
+	drawing_mode (DRAW_MODE_SOLID, NULL, 0, 0);
+
+	setUpdateArea (x - pRadius - blobSize, y - pRadius - blobSize,
+	               (pRadius + blobSize) * 2, (pRadius + blobSize) * 2);
+	requireUpdate ();
+
+	destroy_bitmap (tempBitmap);
 }
diff --git a/src/teleport.h b/src/teleport.h
index c4b2293..2e624ce 100644
--- a/src/teleport.h
+++ b/src/teleport.h
@@ -20,40 +20,56 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  * */
 
-
-#include "main.h"
+#include "globaltypes.h"
 #include "virtobj.h"
 
 class TELEPORT: public VIRTUAL_OBJECT
-  {
+{
   public:
-    int	radius;
-    int	clock;
-    int	startClock;
-    int	peaked;
-    int	dispersing;
-    VIRTUAL_OBJECT *object;
-    TELEPORT *remote;
-
-    virtual ~TELEPORT ();
-    TELEPORT (GLOBALDATA *global, ENVIRONMENT *env, VIRTUAL_OBJECT *targetObj, int destinationX, int destinationY, int objRadius, int duration);
-    TELEPORT (GLOBALDATA *global, ENVIRONMENT *env, TELEPORT *remoteEnd, int destX, int destY);
-    void	initialise ();
-    void	draw (BITMAP *dest);
-    int	applyPhysics ();
-    int	isSubClass (int classNum);
-    inline virtual int	getClass ()
-    {
-      return (TELEPORT_CLASS);
-    }
-    inline virtual void setEnvironment(ENVIRONMENT *env)
-    {
-      if (!_env || (_env != env))
-        {
-          _env = env;
-          _index = _env->addObject (this);
-        }
-    }
-  };
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+	// Source constructor
+	TELEPORT (VIRTUAL_OBJECT *targetObj,
+	          int32_t destinationX, int32_t destinationY,
+	          int32_t objRadius, int32_t duration, int32_t type);
+	virtual ~TELEPORT ();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	void    applyPhysics ();
+	void    draw ();
+
+    eClasses getClass() { return CLASS_TELEPORT; }
+
+
+private:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	// Target constructor
+	TELEPORT (TELEPORT *remoteEnd, int32_t destX, int32_t destY);
+
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	int32_t         clock      = 0;
+	VIRTUAL_OBJECT* object     = nullptr;
+	int32_t         radius     = 0;
+	TELEPORT*       remote     = nullptr;
+	int32_t         startClock = 0;
+};
 
 #endif
diff --git a/src/text.cpp b/src/text.cpp
index 2005792..8120799 100644
--- a/src/text.cpp
+++ b/src/text.cpp
@@ -1,227 +1,307 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cmath>
+#include <algorithm>
 #include "text.h"
+#include "main.h"
 
 
 // Basic constructor to kick things off
 TEXTBLOCK::TEXTBLOCK()
-{
-    complete_text = NULL;
-    total_lines = current_line = 0;
-}
+{ /* nothing to do here */ }
 
 
 // A constructor that also loads text from a file
-TEXTBLOCK::TEXTBLOCK(char *filename)
+TEXTBLOCK::TEXTBLOCK(const char* filename)
 {
-    int status;
-
-    complete_text = NULL;
-    total_lines = current_line = 0;
-    if (filename)
-    {
-        status = Load_File(filename);
-        if (! status)
-          printf("Something went wrong loading text from file: %s\n", filename);
-    }
+	if (filename && !Load_File(filename)) {
+		cerr << "Something went wrong loading text from file:";
+		cerr << filename << " !" << endl;
+	}
 }
 
 
 // clean everything
 TEXTBLOCK::~TEXTBLOCK()
 {
-   int count;
-
-   if (complete_text)
-   {
-     for (count = 0; count < total_lines; count++)
-     {
-        if ( complete_text[count] )
-           free(complete_text[count]);
-     }
-     free(complete_text);
-   }
-
+	destroy();
 }
 
 
+/// @brief unified method to delete the present text
+void TEXTBLOCK::destroy()
+{
+	if (complete_text) {
+		for (int32_t i = 0; i < total_lines; ++i) {
+			if ( complete_text[i] )
+				free(complete_text[i]);
+		}
+		free(complete_text);
+		complete_text = nullptr;
+	}
+}
 
 
-void TEXTBLOCK::Trim_Newline(char *line)
+// This function display all lines of text. Optionally, it
+// will also print line numbers before each line.
+// The number of lines printed is returned.
+int32_t TEXTBLOCK::Display_All(bool show_line_numbers)
 {
-    int index = 0;
-
-    while ( line[index] )
-    {
-       if ( (line[index] == '\n') || (line[index] == '\r') )
-          line[index] = '\0';
-       else
-          index++;
+	int32_t i = 0;
+	for ( ; i < total_lines; ++i) {
+		if (show_line_numbers)
+			printf("%d. ", i);
+		printf("%s\n", complete_text[i]);
     }
+	return i;
 }
 
 
-
-// This function does most of the work. It loads an entire text
-// file into memory. Returns TRUE on success or FALSE if
-// somethign goes wrong.
-int TEXTBLOCK::Load_File(char *filename)
+/// @brief Draw @a text in the box @a region with border and blue background
+/// if @a with_box is true.
+/// This method releases the display and can therefore be used in parallel.
+void draw_text_in_box (BOX* region, const char* text, bool with_box)
 {
-    FILE *phile;
-    int lines_loaded = 0;
-    int we_have_space = 10;
-    char line[MAX_LINE_LENGTH], *status;
-
-    // open the file
-    phile = fopen(filename, "r");
-    if (! phile)
-      return FALSE;
-
-    // give us some space  
-    complete_text = (char **) calloc(we_have_space, sizeof(char *));
-    if (! complete_text)
-    {
-       fclose(phile);
-       return FALSE;
-    }
+	if (with_box) {
+		global.lockLand();
+		rectfill (global.canvas, region->x, region->y, region->w, region->h,
+				  makecol (0,0,128));
+		rect     (global.canvas, region->x, region->y, region->w, region->h,
+				  makecol (128,128,255));
+		global.unlockLand();
+	}
+
+	char     buffer[1024] = { 0 };
+	uint32_t lastSpace    = 0;
+	uint32_t lineBegin    = 0;
+	uint32_t lineCount    = 0;
+	int32_t  lineWidth    = region->w - 27;
+	uint32_t textLength   = static_cast<uint32_t>(strlen(text));
+
+	while (lineBegin < textLength) {
+		uint32_t charCount = 0;
+		uint32_t buffCount = 0;
+		int32_t  buffWidth = 0;
+		memset(buffer, 0, sizeof(char) * 1024);
+
+		// Fill buffer until a line break is found or the maximum width
+		// is exceeded. For the latter case remember the last space found.
+		do {
+			buffer[buffCount] = text[lineBegin + charCount];
+
+			if (buffer[buffCount] == ' ')
+				lastSpace = 0;
+			else
+				++lastSpace;
+
+			if (buffer[buffCount] != '\n')
+				buffCount++;
+
+			charCount++;
+
+			buffWidth = text_length(font, buffer);
+		} while ( text[lineBegin + charCount]
+		       && (buffWidth < lineWidth)
+		       && (buffer[buffCount] != '\n') );
+
+		// If the line width got exceeded, revert to last space seen
+		if (lastSpace && (buffWidth >= lineWidth) ) {
+			charCount -= lastSpace;
+			buffer[buffCount - lastSpace - 1] = 0;
+		} else
+			buffer[buffCount] = 0;
+
+		// Print out the result:
+		if (buffer[0]) {
+			textout_ex (global.canvas, font, buffer, region->x + 5,
+						region->y + (lineCount * env.fontHeight) + 5, WHITE, -1);
+		}
+		lineBegin += charCount;
+		lineCount++;
+	}
+
+	global.make_update (region->x, region->y, region->w, region->h);
+	fi = 1;
+}
 
-    // time to load some text!
-    status = fgets(line, MAX_LINE_LENGTH, phile);
-    while ( (status) && (lines_loaded < MAX_LINES_IN_FILE) && (complete_text)  )
-    {
-        Trim_Newline(line);
-        complete_text[lines_loaded] = (char *) calloc( strlen(line) + 1, sizeof(char));
-        if ( complete_text[lines_loaded] )
-        {
-             strcpy( complete_text[lines_loaded], line );
-             lines_loaded++;
-        }
-        if (lines_loaded >= we_have_space)
-        {
-            we_have_space += 10;
-            complete_text = (char **) realloc( complete_text, we_have_space * sizeof(char *) );
-        }
-        status = fgets(line, MAX_LINE_LENGTH, phile);
-    }       // all done loading
-
-    if (complete_text)
-       complete_text = (char **) realloc( complete_text, lines_loaded * sizeof(char *) );
-    fclose(phile);
-    total_lines = lines_loaded;
-    current_line = 0;
-    return TRUE;
+
+// Returns the current line
+const char* TEXTBLOCK::Get_Current_Line() const
+{
+	return complete_text[current_line];
 }
 
 
+/// @brief Return a specific line or nullptr if @a index is out of bounds
+const char* TEXTBLOCK::Get_Line(int32_t index) const
+{
+	if ( (index > 0) && (index < total_lines))
+		return complete_text[index];
+	return nullptr;
+}
+
 
 // Find a random line and return it
-char *TEXTBLOCK::Get_Random_Line()
+const char* TEXTBLOCK::Get_Random_Line() const
 {
-    int my_line;
-    char *my_text;
-
-    my_line = rand() % total_lines;
-    my_text = complete_text[my_line];
-    return my_text;
+	return complete_text[rand() % total_lines];
 }
 
 
-// Returns the current line
-char *TEXTBLOCK::Get_Current_Line()
+// This function does most of the work. It loads an entire text
+// file into memory. Returns true on success or false if
+// something goes wrong.
+bool TEXTBLOCK::Load_File(const char* filename)
 {
-   char *my_text;
+	char    line[MAX_LINE_LENGTH] = { 0 };
+	int32_t lines_loaded  = 0;
+	int32_t we_have_space = 10;
+
+	// open the file
+	FILE* fIn = fopen(filename, "r");
+	if (!fIn)
+		return false;
+
+	// give us some space
+	destroy();
+	complete_text = (char**)calloc(we_have_space, sizeof(char*));
+	if (!complete_text) {
+		fclose(fIn);
+		return false;
+	}
 
-   my_text = complete_text[current_line];
-   return my_text;
+    // time to load some text!
+	while ( fgets(line, MAX_LINE_LENGTH, fIn)
+	    && (lines_loaded < MAX_LINES_IN_FILE)
+	    && complete_text) {
+
+		// Reserve more space if full
+		if (lines_loaded == we_have_space) {
+			we_have_space += 10;
+
+			char** new_text = (char**)realloc(complete_text, we_have_space * sizeof(char*));
+
+			if (new_text)
+				complete_text = new_text;
+			else {
+				destroy();
+				fclose(fIn);
+				return false;
+			}
+		}
+
+		// Store the line:
+		Trim_Newline(line);
+		complete_text[lines_loaded] = (char*)calloc(strlen(line) + 1, sizeof(char));
+
+		if (complete_text[lines_loaded])
+			strncpy(complete_text[lines_loaded++], line, strlen(line));
+	} // End of loading text from a file
+
+	fclose(fIn);
+	total_lines = lines_loaded;
+	current_line = 0;
+
+	return true;
 }
 
 
+int32_t TEXTBLOCK::Lines() const
+{
+	return total_lines;
+}
 
 
 // Move the line counter ahead, if possible
-// Returns FALSE if we hit the end of lines, or
+// Returns false if we hit the end of lines, or
 // true if everything is OK
-int TEXTBLOCK::Next_Line()
+bool TEXTBLOCK::Next_Line()
 {
-    current_line++;
-    if (current_line >= total_lines)
-    {
-        current_line = total_lines - 1;
-        return FALSE;
-    }
-    return TRUE;
+	if (++current_line >= total_lines) {
+		current_line = total_lines - 1;
+		return false;
+	}
+	return true;
 }
 
 
-
 // Go to the previous line. The function returns
-// TRUE is everything is OK. If we were already
-// at the beginning, then FALSE is returned.
-int TEXTBLOCK::Previous_Line()
+// true is everything is OK. If we were already
+// at the beginning, then false is returned.
+bool TEXTBLOCK::Previous_Line()
 {
-    if (current_line > 0)
-    {
-         current_line--;
-         return TRUE;
+    if (--current_line < 0) {
+		current_line = 0;
+		return false;
     }
-    return FALSE;
+    return true;
 }
 
 
-// This function display all lines of text. Optionally, it
-// will also print line numbers before each line.
-// The number of lines printed is returned.
-int TEXTBLOCK::Display_All(int line_numbers)
+// This method renders a part of the text to global.canvas
+void TEXTBLOCK::Render_Lines(int32_t scrollOffset, int32_t spacing,
+                             int32_t top, int32_t bottom)
 {
-    int counter = 0;
-
-    while (counter < total_lines)
-    {
-        if (line_numbers)
-           printf("%d. ", counter);
-        printf("%s\n", complete_text[counter]);
-        counter++;
-    }
-    return counter;
+	int32_t txtheight = env.fontHeight * spacing ;
+	int32_t xPos      = env.halfWidth;
+	int32_t yPos      = env.halfHeight + scrollOffset;
+	for ( int32_t i = 0
+	    ; (i < total_lines) && (yPos < env.screenHeight)
+	    ; ++i) {
+
+	    if ( (yPos > (top - txtheight)) && (yPos < (bottom + txtheight)) ) {
+			textout_centre_ex (global.canvas, font, complete_text[i],
+			                   xPos + 2, yPos + 2, BLACK, -1);
+			textout_centre_ex (global.canvas, font, complete_text[i],
+			                   xPos,     yPos,     WHITE, -1);
+	    }
+		yPos += txtheight;
+	}
 }
 
 
+// This is a free floating function
+const char* Add_Comma(int32_t number)
+{
+	static char return_value[128] = { 0 };
+	memset(return_value, 0, 128);
 
+	// MAX_MONEY_IN_WALLET is: 1,000,000,000 = 13 characters
+	char buffer[15] = { 0 };
+	snprintf(buffer, 14, "%d", number);
 
+	int32_t index       = strlen(buffer); // start from the end
+	int32_t returnindex = index + (index / 3) - 1;
+	int32_t th_count    = 0;
 
+	if (0 == (index % 3) )
+		returnindex--;
 
+	while (--index >= 0) {
+		return_value[returnindex--] = buffer[index];
+		th_count++;
 
-// This is a free floating function
-char *Add_Comma(int number)
+		if ( (th_count == 3) && (returnindex > 0) ) {
+			th_count = 0;
+			return_value[returnindex] = ',';
+			returnindex--;
+		}
+	}
+
+	return return_value;
+}
+
+
+void Trim_Newline(char *line)
 {
-    char buffer[64];
-    static char return_value[128];
-    int index, return_index;
-    int place_counter = 0;
-
-    // we won't worry about over-flow with such a big buffer
-    sprintf(buffer, "%d", number);
-    memset(return_value, '\0', 128);
-    index = strlen(buffer);      // start from the end
-    return_index = index + (index / 3);
-    return_index -= 1;
-    if (! (index % 3) )
-       return_index--;
-    index -= 1;
-    while (index >= 0)
-    {
-        return_value[return_index] = buffer[index];
-        return_index--;
-        index--;
-        place_counter++;
-        if ( (place_counter == 3) && (return_index > 0) )
-        {
-            place_counter = 0;
-            return_value[return_index] = ',';
-            return_index--;
-        }
-    }
-    return return_value;
+	int32_t index = 0;
+
+	while ( line[index] ) {
+		if ( (line[index] == '\n') || (line[index] == '\r') )
+			line[index] = '\0';
+		else
+			++index;
+	}
 }
 
diff --git a/src/text.h b/src/text.h
index 97055e7..eb38fbe 100644
--- a/src/text.h
+++ b/src/text.h
@@ -11,36 +11,72 @@
  * -- Jesse
 */
 
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
+#include <cstdint>
 
 #define MAX_LINE_LENGTH 512
 #define MAX_LINES_IN_FILE 1024
 
+struct BOX;
+
+/// @brief alignment of texts
+enum alignType {
+	CENTRE = 0,
+	LEFT,
+	RIGHT
+};
+
 
 class TEXTBLOCK
 {
 public:
-   int total_lines, current_line;
-   char **complete_text;
 
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+
+	TEXTBLOCK ();
+	TEXTBLOCK (const char* filename);
+	~TEXTBLOCK();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	int32_t     Display_All     (bool show_line_numbers); // display all text
+	const char* Get_Current_Line() const;                 // return current line
+	const char* Get_Line        (int32_t index) const;    // return a specific line
+	const char* Get_Random_Line () const;                 // give us a random line
+	bool        Load_File       (const char *filename);   // load lines from a file
+	int32_t     Lines           () const;                 // Return number of total lines
+	bool        Next_Line       ();                       // advance the current line
+	bool        Previous_Line   ();                       // move the current line back
+	void        Render_Lines    (int32_t scrollOffset,
+	                             int32_t spacing,
+	                             int32_t top,
+	                             int32_t bottom);         // Render to global.canvas
+
+
+private:
+
+	/* -----------------------
+	 * --- Private methods ---
+	 * -----------------------
+	 */
+
+	void destroy();
 
-   TEXTBLOCK();
-   TEXTBLOCK(char *filename);
-   ~TEXTBLOCK();
 
-   void Trim_Newline(char *line);     // hack the newline off a string
-   int Load_File(char *filename);    // load lines from a file
-   char *Get_Random_Line();   // give us a random line
-   char *Get_Current_Line();  // return current line
-   int Next_Line();           // advance the current line counter one
-   int Previous_Line();       // move the current line counter back one
-   int Display_All(int line_numbers); // display all text
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
 
+	int32_t total_lines   = 0;
+	int32_t current_line  = 0;
+	char**  complete_text = nullptr;
 };
 
 
@@ -50,10 +86,13 @@ public:
 
 // This function returns a string with
 // comma characters between thousands positions
-// There is no need to free the returned string.
-char *Add_Comma(int number);
+// You *MUST* *NOT* free the returned string.
+const char* Add_Comma(int32_t number);
 
+void draw_text_in_box(BOX* region, const char* text, bool with_box);
 
+// hack the newline off a string
+void Trim_Newline(char *line);
 
 #endif
 
diff --git a/src/update.cpp b/src/update.cpp
index e7966ee..d7d35db 100644
--- a/src/update.cpp
+++ b/src/update.cpp
@@ -1,105 +1,104 @@
-#ifdef NETWORK
-#ifdef THREADS
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <unistd.h>
-
-#include "update.h"
-
-
-
-
-
-
-char *Check_For_Update(char *server_name, char *remote_file, char *host_name, char *current_version)
-{
-    struct update_data *my_update;
-    pthread_t update_thread;
-
-    my_update = (struct update_data *) calloc(1, sizeof(struct update_data));
-    if (!my_update)
-       return NULL;
-    my_update->server_name = server_name;
-    my_update->remote_file = remote_file;
-    my_update->host_name = host_name;
-    my_update->current_version = current_version;
-    my_update->update_string = (char *) calloc(UPDATE_STR_LENGTH, sizeof(char));
-    if (! my_update->update_string)
-       return NULL;
-
-    pthread_create( &update_thread, NULL, Get_Latest_Version, (void *) my_update);
-    return my_update->update_string;
-}
-
-
-
-void *Get_Latest_Version(void *new_data)
-{
-   struct update_data *my_data = (struct update_data *) new_data;
-   // set up socket
-   int socket_num, port_number = 80;
-   struct sockaddr_in server_address;
-   struct hostent *server;
-   char buffer[1024];
-   char *found = NULL;
-   int got_bytes;
-   double this_version, web_version;
-
-
-   socket_num = socket(AF_INET, SOCK_STREAM, 0);
-   if (socket_num < 0)
-      pthread_exit(NULL);
-   server = gethostbyname(my_data->server_name);
-   if (! server)
-      pthread_exit(NULL);
-   bzero((char *) &server_address, sizeof(server_address));
-   server_address.sin_family = AF_INET;
-   bcopy((char *) server->h_addr,
-         (char *) &server_address.sin_addr.s_addr,
-         server->h_length);
-   server_address.sin_port = htons(port_number);
-
-   // try to connect
-   if ( connect(socket_num, (sockaddr *)&server_address, sizeof(server_address)) < 0)
-      pthread_exit(NULL);
-
-
-   // get HTTP data
-   snprintf(buffer, 1024, "GET /%s HTTP/1.1\nHost: %s\n\n", my_data->remote_file, my_data->host_name);
-   write(socket_num, buffer, strlen(buffer));
-   got_bytes = read(socket_num, buffer, 1024);
-
-   // search for version number in return data
-   if (got_bytes > 1)
-      found = strstr(buffer, "Version: ");
-   while ( (got_bytes > 1) && (! found) )
-   {
-      got_bytes = read(socket_num, buffer, 1024);
-      if (got_bytes > 1)
-         found = strstr(buffer, "Version: ");
-   }
-
-   // compare version number
-   if (found)
-   {
-      found += 9;
-      found[5] = '\0';
-      sscanf(found, "%lf", &web_version);
-      sscanf(my_data->current_version, "%lf", &this_version);
-      if (web_version > this_version)
-         sprintf(my_data->update_string, "A new version, %2.1lf, is ready for download.", web_version);
-   }
-
-   close(socket_num);
-   pthread_exit(NULL);
-}
-
-#endif
-#endif
+#include "debug.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sys/types.h>
+#include <cerrno>
+
+#if defined(ATANKS_IS_MSVC)
+/// What is needed here?
+#else
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <netdb.h>
+#  include <unistd.h>
+#endif // MSVC++ versus gcc/clang
+
+#include "update.h"
+#include "network.h"
+#include "externs.h"
+
+/// @brief update_data default ctor
+update_data::update_data(const char* server_, const char* remote_,
+	                     const char* host_,   const char* version_) :
+	server_name    (server_  ? strdup(server_)  : strdup("")),
+	host_name      (host_    ? strdup(host_)    : strdup("")),
+	remote_file    (remote_  ? strdup(remote_)  : strdup("")),
+	current_version(version_ ? strdup(version_) : strdup(""))
+{
+	memset(update_string, 0, sizeof(char) * 1024);
+}
+
+/// @brief update_data default dtor
+update_data::~update_data()
+{
+	if (server_name)     free (server_name);
+	if (host_name)       free (host_name);
+	if (remote_file)     free (remote_file);
+	if (current_version) free (current_version);
+}
+
+
+void update_data::operator()()
+{
+#ifdef NETWORK
+	if (env.check_for_updates) {
+		// set up socket
+		int socket_num, port_number = 80;
+		struct sockaddr_in server_address;
+		struct hostent* server;
+		char buffer[1024];
+		char* found = nullptr;
+		int got_bytes;
+		double this_version, web_version;
+		int32_t towrite, written;
+
+
+		socket_num = socket(AF_INET, SOCK_STREAM, 0);
+		if (socket_num < 0)
+			return;
+		server = gethostbyname(server_name);
+		if (! server)
+			return;
+		bzero((char*) &server_address, sizeof(server_address));
+		server_address.sin_family = AF_INET;
+		bcopy((char*) server->h_addr,
+			  (char*) &server_address.sin_addr.s_addr,
+			  server->h_length);
+		server_address.sin_port = htons(port_number);
+
+		// try to connect
+		if ( connect(socket_num, (sockaddr*)&server_address, sizeof(server_address)) < 0)
+			return;
+
+
+		// get HTTP data
+		SAFE_WRITE(socket_num, "GET /%s HTTP/1.1\nHost: %s\n\n",
+					remote_file, host_name)
+
+		got_bytes = read(socket_num, buffer, 1024);
+
+		// search for version number in return data
+		if (got_bytes > 1)
+			found = strstr(buffer, "Version: ");
+		while ( (got_bytes > 1) && (! found) ) {
+			got_bytes = read(socket_num, buffer, 1024);
+			if (got_bytes > 1)
+				found = strstr(buffer, "Version: ");
+		}
+
+		// compare version number
+		if (found) {
+			found += 9;
+			found[5] = '\0';
+			sscanf(found, "%lf", &web_version);
+			sscanf(current_version, "%lf", &this_version);
+			if (web_version > this_version)
+				snprintf(update_string, 1024, "A new version, %2.1lf, is ready for download.", web_version);
+		}
+
+		close(socket_num);
+	}
+#endif // NETWORK
+}
diff --git a/src/update.h b/src/update.h
index 2f5ec4b..058843b 100644
--- a/src/update.h
+++ b/src/update.h
@@ -1,43 +1,25 @@
-#ifndef UPDATE_HEADER_FILE__
-#define UPDATE_HEADER_FILE__
-
-
-#ifdef NETWORK
-#ifdef THREADS
-
-
-#define UPDATE_STR_LENGTH 256
-
-
-struct update_data
-{
-   char *server_name, *host_name;
-   char *remote_file;
-   char *update_string;
-   char *current_version;
-};
-
-
-
-// this function kicks it all off
-// returns the address of a string containing
-// update notice. The string will have a notice
-// if update is ready or be 0-length with no update.
-// The function is threaded and may change the
-// string after the function returns.
-// The returning string is allocated and should
-// be freed at the end of the calling process.
-// On error, NULL is returned.
-char *Check_For_Update(char *server_name, char *remote_file, char *host_name, char *current_version);
-
-
-
-// The function/thread that checks for new updates.
-void *Get_Latest_Version(void *data);
-
-
-#endif
-#endif
-
-#endif
-
+#ifndef UPDATE_HEADER_FILE__
+#define UPDATE_HEADER_FILE__
+
+
+#define UPDATE_STR_LENGTH 256
+
+
+// rewritten struct to be used with C++11 threads.
+struct update_data
+{
+	char* server_name         = nullptr;
+	char* host_name           = nullptr;
+	char* remote_file         = nullptr;
+	char  update_string[1024];
+	char* current_version     = nullptr;
+
+	explicit update_data(const char* server_, const char* remote_,
+	                     const char* host_,   const char* version_);
+	~update_data();
+
+	void operator()();
+};
+
+#endif
+
diff --git a/src/virtobj.cpp b/src/virtobj.cpp
index 239d409..4397b9c 100644
--- a/src/virtobj.cpp
+++ b/src/virtobj.cpp
@@ -17,196 +17,170 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  * */
 
+#include <cassert>
 #include "virtobj.h"
 #include "environment.h"
 
+VIRTUAL_OBJECT::VIRTUAL_OBJECT() :
+	needsUpdate(ATOMIC_VAR_INIT(false))
+{ /* nothing to do */ }
 
 
-VIRTUAL_OBJECT::VIRTUAL_OBJECT()
-    {
-      _bitmap = NULL;
-      _env = NULL;
-      _global = NULL;
-      x = 0;
-      y = 0;
-      xv = yv = 0;
-      angle = 0;
-      destroy = FALSE;
-      age = 0;
-      maxAge = -1;
-      physics = 0;
-      _current.x = 0;
-      _current.y = 0;
-      _current.w = 0;
-      _current.h = 0;
-      _old.x = 0;
-      _old.y = 0;
-      _old.w = 0;
-      _old.h = 0;
-    }
 
 
+VIRTUAL_OBJECT::~VIRTUAL_OBJECT ()
+{
+	bitmap = nullptr;
+}
 
 
-VIRTUAL_OBJECT::~VIRTUAL_OBJECT ()
-      {
-        _bitmap = NULL;
-        // player, _env and _global must be handled by descendants!
-      }
+void VIRTUAL_OBJECT::addUpdateArea (int32_t left, int32_t top,
+                                    int32_t width, int32_t height)
+{
+	if (left < dim_cur.x)
+		dim_cur.x = left;
+	if (top < dim_cur.y)
+		dim_cur.y = top;
+	/* This is prone to the following error:
+	   If left is greater than dim_cur.x but the width is
+	   smaller than dim_cur.w, (left + width) can
+	   nevertheless end up right of (dim_cur.x + dim_cur.w).
+	   Setting dim_cur.w to 'width' in that case makes
+	   the update area smaller not larger.
+	   The same applies to the height.
+	   - Sven
+	if ((left + width) > (dim_cur.x + dim_cur.w))
+		dim_cur.w = width;
+	if ((top + height) > (dim_cur.y + dim_cur.h))
+		dim_cur.h = height;
+	*/
+	int32_t new_r = left + width;
+	int32_t new_b = top + height;
+	int32_t old_r = dim_cur.x + dim_cur.w;
+	int32_t old_b = dim_cur.y + dim_cur.h;
+	if (new_r > old_r)
+		dim_cur.w = new_r - dim_cur.x + 1;
+	if (new_b > old_b)
+		dim_cur.h = new_b - dim_cur.y + 1;
+}
 
 
+void VIRTUAL_OBJECT::applyPhysics ()
+{
+	x += xv;
+	y += yv;
+}
+
+
+void VIRTUAL_OBJECT::draw ()
+{
+	assert(bitmap && "ERROR: VIRTUAL_OBJECT::draw() called without bitmap!");
+
+	if (!destroy && bitmap) {
+
+		rotate_sprite (global.canvas, bitmap,
+		               x - (width / 2),
+		               y - (height / 2),
+		               itofix (angle));
+
+		// The update area depends on the rotation state (aka angle)
+		if (angle) {
+			int32_t length =  std::max(width, height)
+			               + (std::min(width, height) / 2);
+			setUpdateArea(x - (length/2),
+			              y - (length/2),
+			              length, length);
+		} else
+			setUpdateArea(x - (width / 2) - 1,
+			              y - (height / 2) - 1,
+			              width + 2, height + 2);
+		requireUpdate ();
+	}
+}
+
+
+void VIRTUAL_OBJECT::initialise ()
+{
+	age    = 0;
+	maxAge = -1;
+	x      = 0;
+	y      = 0;
+	xv     = 0;
+	yv     = 0;
+	destroy = false;
+	dim_cur = dim_old = BOX();
+}
+
+
+/// @brief Set a new bitmap and store width and height for easy drawing.
+void VIRTUAL_OBJECT::setBitmap(BITMAP* bitmap_)
+{
+	if (bitmap_ != bitmap) {
+		bitmap = bitmap_;
+
+		if (bitmap) {
+			height = bitmap->h;
+			width  = bitmap->w;
+		} else {
+			height = 0;
+			width  = 0;
+		}
+	}
+}
+
+
+void VIRTUAL_OBJECT::setUpdateArea (int32_t left, int32_t top,
+                                    int32_t width, int32_t height)
+{
+	dim_cur.x = left;
+	dim_cur.y = top;
+	dim_cur.w = width;
+	dim_cur.h = height;
+}
+
 
 /** @brief update
   *
-  * This method triggers an update of the canvas (aka drawing area) with the dimensions and position
-  * of this object.
+  * This method triggers an update of the canvas (aka drawing area) with the
+  * dimensions and position of this object.
   */
 void VIRTUAL_OBJECT::update()
 {
-  if (_requireUpdate)
-    {
-      int changed;
-
-      if (_current.w)
-        {
-          if (_align == LEFT)
-            {
-              _env->make_update (_current.x, _current.y,
-                                 _current.w, _current.h);
-            }
-          else if (_align == RIGHT)
-            {
-              _env->make_update (_current.x - _current.w,
-                                 _current.y - _current.h,
-                                 _current.w, _current.h);
-            }
-          else
-            {
-              _env->make_update (_current.x - (_current.w / 2),
-                                 _current.y - (_current.h / 2),
-                                 _current.w + 2, _current.h + 2);
-            }
-        }
-
-      if ((_old.x == _current.x) &&
-          (_old.y == _current.y) &&
-          (_old.w == _current.w) &&
-          (_old.h == _current.h))
-        changed = FALSE;
-      else
-        changed = TRUE;
-
-      if (changed)
-        {
-          if (_old.w)
-            {
-              if (_align == LEFT)
-                {
-                  _env->make_update (_old.x, _old.y,
-                                     _old.w, _old.h);
-                }
-              else if (_align == RIGHT)
-                {
-                  _env->make_update (_old.x - _old.w,
-                                     _old.y - _old.h,
-                                     _old.w, _old.h);
-                }
-              else
-                {
-                  _env->make_update (_old.x - (_old.w / 2),
-                                     _old.y - (_old.h / 2),
-                                     _old.w + 2, _old.h + 2);
-                }
-            }
-          _old.x = _current.x;
-          _old.y = _current.y;
-          _old.w = _current.w;
-          _old.h = _current.h;
-        }
-      _requireUpdate = FALSE;
-    }
+	if (!needsUpdate.load(ATOMIC_READ))
+		return;
+
+	// Add update area for the current dimension
+	if (dim_cur.w > 0) {
+		int32_t left   = LEFT  == align ? dim_cur.x
+		               : RIGHT == align ? dim_cur.x -  dim_cur.w
+		               :                  dim_cur.x - (dim_cur.w / 2);
+		int32_t top    = LEFT  == align ? dim_cur.y
+		               : RIGHT == align ? dim_cur.y -  dim_cur.h
+		               :                  dim_cur.y - (dim_cur.h / 2);
+		int32_t right  = std::min(env.screenWidth, left + dim_cur.w + 2);
+		int32_t bottom = std::min(env.screenHeight, top + dim_cur.h + 2);
+
+		if ( (right > left) && (bottom > top) )
+			global.make_update(left, top, right - left, bottom - top);
+	} // End of updating current area
+
+	// If the dimensions changed, the old area needs an update, too
+	if ( (dim_old.w > 0) && (dim_old != dim_cur) ) {
+		int32_t left   = LEFT  == align ? dim_old.x
+		               : RIGHT == align ? dim_old.x -  dim_old.w
+		               :                  dim_old.x - (dim_old.w / 2);
+		int32_t top    = LEFT  == align ? dim_old.y
+		               : RIGHT == align ? dim_old.y -  dim_old.h
+		               :                  dim_old.y - (dim_old.h / 2);
+		int32_t right  = std::min(env.screenWidth, left + dim_old.w + 2);
+		int32_t bottom = std::min(env.screenHeight, top + dim_old.h + 2);
+
+		if ( (right > left) && (bottom > top) )
+			global.make_update(left, top, right - left, bottom - top);
+	} // End of updating old area
+
+	dim_old = dim_cur;
+
+	needsUpdate.store(false, ATOMIC_WRITE);
 }
 
-
-void VIRTUAL_OBJECT::initialise ()
-    {
-      age = 0;
-      maxAge = -1;
-      x = 0;
-      y = 0;
-      xv = 0;
-      yv = 0;
-      destroy = false;
-      _current.x = 0;
-      _current.y = 0;
-      _current.w = 0;
-      _current.h = 0;
-      _old.x = 0;
-      _old.y = 0;
-      _old.w = 0;
-      _old.h = 0;
-    }
-
-
-
-int VIRTUAL_OBJECT::applyPhysics ()
-    {
-      x += xv;
-      y += yv;
-
-      return (0);
-    }
-
-
-
-void VIRTUAL_OBJECT::draw (BITMAP *dest)
-    {
-      if (!destroy)
-        {
-          rotate_sprite (dest, _bitmap,
-                         (int)x - (_bitmap->w / 2),
-                         (int)y - (_bitmap->h / 2),
-                         itofix (angle));
-          if (angle == 0)
-            {
-              setUpdateArea ((int)x - (_bitmap->w / 2 + 1),
-                             (int)y - (_bitmap->h / 2 + 1),
-                             _bitmap->w + 2, _bitmap->h + 2);
-            }
-          else
-            {
-              int length = MAX (_bitmap->w, _bitmap->h);
-              length += MIN (_bitmap->w, _bitmap->h) / 2;
-              setUpdateArea ((int)x - (length/2),
-                             (int)y - (length/2),
-                             length, length);
-            }
-          requireUpdate ();
-        }
-    }
-
-
-
-
-void VIRTUAL_OBJECT::setUpdateArea (int left, int top, int width, int height)
-    {
-      _current.x = left;
-      _current.y = top;
-      _current.w = width;
-      _current.h = height;
-    }
-
-
-
-void VIRTUAL_OBJECT::addUpdateArea (int left, int top, int width, int height)
-    {
-      if (left < _current.x)
-        _current.x = left;
-      if (top < _current.y)
-        _current.y = top;
-      if (left + width > _current.x + _current.w)
-        _current.w = width;
-      if (top + height > _current.y + _current.h)
-        _current.h = height;
-    }
-
diff --git a/src/virtobj.h b/src/virtobj.h
index e8fc9a4..fd2e064 100644
--- a/src/virtobj.h
+++ b/src/virtobj.h
@@ -24,70 +24,139 @@
 #define _PURE =0
 #endif // _PURE
 
-enum    alignType { CENTRE, LEFT, RIGHT };
-
 #include "main.h"
+#include "text.h"
+
+
+/// @enum ePhysType
+/// @brief Determine which kind of physics should be used
+enum ePhysType
+{
+	PT_NORMAL = 0,  //!< No special processing, just a normal curve shot and impact.
+	PT_FUNKY_FLOAT, //!< Funky bomb-lets ignore gravitation.
+	PT_DIGGING,     //!< Burrowers and the like dig through dirt in a reverse curve.
+	PT_ROLLING,     //!< Roll over the surface.
+	PT_DIRTBOUNCE,  //!< Dirt debris bounces off of dirt and all walls.
+	PT_SMOKE,       //!< Smoke reacts on nothing but repulsor shields.
+	PT_NONE,        //!< Special values for age caused detonation triggering.
+};
+
 
-#include "player.h"
+#ifndef HAS_PLAYER
+class PLAYER;
+#endif // HAS_PLAYER
 
 class VIRTUAL_OBJECT
-  {
-  public:
-    BOX	_current, _old;
-    alignType	_align;
-    BITMAP	*_bitmap;
-    ENVIRONMENT	*_env;
-    GLOBALDATA	*_global;
-    int	_requireUpdate;
-    int	_index;
-    PLAYER *player;
-    double	x, y;
-    double	xv, yv;
-    int	angle;
-    int	destroy;
-    int	age, maxAge;
-    int	physics;	// Special physics processing?
-
-    /* --- constructor --- */
-    VIRTUAL_OBJECT();
-
-    /* --- destructor --- */
-    virtual	~VIRTUAL_OBJECT ();
-
-    /* --- non-inline methods --- */
-    void	update();
-
-    /* --- pure virtual (abstract) methods --- */
-    virtual int	  isSubClass (int classNum)_PURE;
-    virtual int	  getClass ()_PURE;
-    virtual void  setEnvironment(ENVIRONMENT *env)_PURE; // This is virtual, so objects add themselves to _env!
-
-    /* --- inline methods --- */
-    void	requireUpdate ()
-    {
-      _requireUpdate = TRUE;
-    }
-
-    virtual void	initialise ();
-
-    virtual int	applyPhysics ();
-
-    inline void	setGlobalData (GLOBALDATA *global)
-    {
-      if (!_global || (_global != global))
-        _global = global;
-    }
-
-virtual void	draw (BITMAP *dest);
-
-void	setUpdateArea (int left, int top, int width, int height);
-
-void	addUpdateArea (int left, int top, int width, int height);
-
-    inline int	getIndex ()
-    {
-      return (_index);
-    }
-  };
+{
+public:
+
+	/* -----------------------------------
+	 * --- Constructors and destructor ---
+	 * -----------------------------------
+	 */
+	explicit VIRTUAL_OBJECT();
+	virtual	~VIRTUAL_OBJECT();
+
+
+	/* ----------------------
+	 * --- Public methods ---
+	 * ----------------------
+	 */
+
+	/* --- non-inline methods --- */
+	void         addUpdateArea (int32_t left,  int32_t top,
+	                            int32_t width, int32_t height);
+	virtual void applyPhysics  ();
+	virtual void draw          ();
+	virtual void initialise    ();
+	void         setUpdateArea (int32_t left, int32_t top,
+	                            int32_t width, int32_t height);
+	void         update        ();
+
+	/* --- inline methods --- */
+	void         requireUpdate () { needsUpdate.store(true, ATOMIC_WRITE); }
+
+	/* --- pure virtual (abstract) methods --- */
+	virtual eClasses getClass  ()  _PURE;
+
+
+	/* ------------------------------
+	 * --- templated list getters ---
+	 * ------------------------------
+	 */
+
+	/// @brief If not nullptr, set @a prev_ to the predecessor of this.
+	template<typename obj_T>
+	void getPrev(obj_T** prev_)
+	{
+		obj_T* prev_obj = static_cast<obj_T*>(prev);
+		if (prev_)
+			*prev_ = prev_obj;
+	}
+
+	/// @brief If not nullptr, set @a next_ to the successor of this.
+	template<typename obj_T>
+	void getNext(obj_T** next_)
+	{
+		obj_T* next_obj = static_cast<obj_T*>(next);
+		if (next_)
+			*next_ = next_obj;
+	}
+
+	/* ----------------------
+	 * --- Public members ---
+	 * ----------------------
+	 */
+
+	bool            destroy = false;
+	VIRTUAL_OBJECT* next    = nullptr;
+	PLAYER*         player  = nullptr;
+	VIRTUAL_OBJECT* prev    = nullptr;
+	double          x       = 0.;
+	double          y       = 0.;
+
+protected:
+
+
+	/* -------------------------
+	 * --- Protected methods ---
+	 * -------------------------
+	 */
+
+	BITMAP* getBitmap() const { return bitmap; }
+	bool            hasBitmap() const { return (bitmap != nullptr); }
+	void            setBitmap(BITMAP* bitmap_);
+
+
+	/* -------------------------
+	 * --- Protected members ---
+	 * -------------------------
+	 */
+
+	int32_t   age      = 0;
+	alignType align    = LEFT;
+	int32_t   angle    = 0;
+	BOX       dim_cur;
+	BOX       dim_old;
+	int32_t   height   = 0;
+	int32_t   maxAge   = -1;
+	ePhysType physType = PT_NORMAL; // Special physics processing?
+	int32_t   width    = 0;
+	double    xv       = 0.;
+	double    yv       = 0.;
+
+private:
+
+	/* -----------------------
+	 * --- Private members ---
+	 * -----------------------
+	 */
+
+	BITMAP* bitmap = nullptr;
+	abool_t         needsUpdate;
+};
+
+/// === Shorten the usage of virtual objects ===
+typedef VIRTUAL_OBJECT vobj_t;
 
 #endif // VIRTOBJ_DEFINE
diff --git a/src/winclock.h b/src/winclock.h
new file mode 100644
index 0000000..34d1b6e
--- /dev/null
+++ b/src/winclock.h
@@ -0,0 +1,105 @@
+#pragma once
+#ifndef ATANKS_SRC_WINCLOCK_H_INCLUDED
+#define ATANKS_SRC_WINCLOCK_H_INCLUDED
+
+/* Workaround for the buggy <chrono> implementation of VS12.
+ *
+ * I do not know whether this is more embarrassing for me or Microsoft.
+ * For me, because I didn't realize this before it was too late, I guess.
+ * The steady_clock is not steady, and the high_resolution_clock is not
+ * high resolution.
+ * This will be fixed in VS13, but while writing this only a community
+ * preview has been published. This preview is noted to be not production
+ * ready and should not be installed on a system that can't be formatted.
+ *
+ * So until I have a full production ready VS13 (Visual Studio 2015), this
+ * header works around the buggy <chrono> implementation.
+ *
+ * For more information see:
+ * https://connect.microsoft.com/VisualStudio/feedback/details/858357/
+ * and
+ * https://connect.microsoft.com/VisualStudio/feedback/details/753115/
+ */
+
+#if defined(ATANKS_IS_MSVC)
+#include "main.h"
+
+#ifdef USE_MUTEX_INSTEAD_OF_SPINLOCK
+#  include <mutex>
+#  define CSpinLock std::mutex
+#endif // USE_MUTEX_INSTEAD_OF_SPINLOCK
+
+// timer variable and locker
+bool       has_win_clock  = false;
+CSpinLock  win_clock_lock;
+volatile
+int32_t    win_clock      = 0;
+
+// additional functions:
+void win_clock_add()
+{
+	++win_clock;
+}
+END_OF_FUNCTION(win_clock_add)
+
+void win_clock_deinit()
+{
+	if ( has_win_clock ) {
+		remove_int(win_clock_add);
+		has_win_clock = false;
+	}
+	win_clock = 0;
+}
+
+inline int32_t win_clock_get()
+{
+	win_clock_lock.lock();
+	int32_t result = win_clock;
+	win_clock = 0;
+	win_clock_lock.unlock();
+	return result;
+}
+
+void win_clock_init()
+{
+	win_clock = 0;
+	if ( !has_win_clock ) {
+		LOCK_VARIABLE(win_clock)
+		LOCK_FUNCTION(win_clock_add)
+		install_int_ex(win_clock_add, MSEC_TO_TIMER(1));
+		has_win_clock = true;
+	}
+}
+
+// Re-implement the millisecond sensitive functions:
+int32_t game_us_get()
+{
+	return win_clock_get() * 1000;
+}
+
+
+void game_us_reset()
+{
+	win_clock_lock.lock();
+	win_clock = 0;
+	win_clock_lock.unlock();
+}
+
+
+int32_t menu_ms_get()
+{
+	return win_clock_get();
+}
+
+
+void menu_ms_reset()
+{
+	win_clock_lock.lock();
+	win_clock = 0;
+	win_clock_lock.unlock();
+}
+
+#endif // defined(ATANKS_IS_MSVC)
+
+#endif // ATANKS_SRC_WINCLOCK_H_INCLUDED
+
diff --git a/tankgun/2.bmp b/tankgun/2.bmp
index c2b6829..586fa88 100644
Binary files a/tankgun/2.bmp and b/tankgun/2.bmp differ
diff --git a/tankgun/4.bmp b/tankgun/4.bmp
index db4b30c..f22c46b 100644
Binary files a/tankgun/4.bmp and b/tankgun/4.bmp differ
diff --git a/tankgun/5.bmp b/tankgun/5.bmp
index 7b36a8c..5242789 100644
Binary files a/tankgun/5.bmp and b/tankgun/5.bmp differ
diff --git a/tankgun/7.bmp b/tankgun/7.bmp
index 0162b14..04a44c3 100644
Binary files a/tankgun/7.bmp and b/tankgun/7.bmp differ
diff --git a/text/panic.pt_BR.txt b/text/panic.pt_BR.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic.pt_BR.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/panic.txt b/text/panic.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/panic_ES.txt b/text/panic_ES.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic_ES.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/panic_de.txt b/text/panic_de.txt
new file mode 100644
index 0000000..952552a
--- /dev/null
+++ b/text/panic_de.txt
@@ -0,0 +1,8 @@
+%s WILL MICH TOT SEHEN!
+%s IST HINTER MIR HER!
+HELFT MIR GEGEN %s!
+NEIN, %s, OH NEIN!
+%s ZIELT NUR AUF MICH!
+%s NIMMT MICH AUSEINANDER!
+%s ZERFETZT MICH GERADE!
+JETZT SCHIESST DU, %s, JA?
diff --git a/text/panic_fr.txt b/text/panic_fr.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic_fr.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/panic_it.txt b/text/panic_it.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic_it.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/panic_ru.txt b/text/panic_ru.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic_ru.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/panic_sk.txt b/text/panic_sk.txt
new file mode 100644
index 0000000..44f5ee7
--- /dev/null
+++ b/text/panic_sk.txt
@@ -0,0 +1,8 @@
+%s WANTS ME DEAD!
+%s IS COMING FOR ME!
+HELP ME AGAINST %s!
+NO, %s, NO!
+%s ONLY AIMS AT ME!
+%s IS TAKING ME DOWN!
+%s IS RIPPING ME APART!
+NOW YOU SHOOT ME, %s, YEAH?
diff --git a/text/weapons.pt_BR.txt b/text/weapons.pt_BR.txt
index 484f817..fea87f7 100644
--- a/text/weapons.pt_BR.txt
+++ b/text/weapons.pt_BR.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Míssil Pequeno
 Produces a low impact explosion
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Míssil Médio
 Low yield, small area explosion
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Míssil Grande
 Medium yield, medium area explosion
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Small Nuke
 High yield, large area explosion
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Nuke
 Very high yield, huge explosion
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Death Head
 Massive, deadly explosion
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Spread
 5 Small missiles in a single shot
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Spread
 5 Medium missiles in a single shot
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Spread
 5 Large missiles in a single shot
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Super Spread
 4 Small Nukes in a single shot
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Death Spread
 3 Nukes in a single shot
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armageddon
 3 Death Heads in a single shot
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Chain Missile
 Fires a volly of three small missiles
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Chain Gun
 Fires a volly of five small missiles
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Jack Hammer
 Fires a volly of eight small missiles
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Shaped Charge
 High yield horizontal explosion. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Wide Boy
 Devastating horizontal explosion. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cutter
 Horizontal explosion that will obliterate anything that gets in the way. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion in comparison to a similar sized uncontrolled blast.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Small Roller
 Medium explosive which rolls downhill until it hits something
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Roller
 Large explosive which rolls downhill until it hits something
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Death Roller
 Huge explosive which rolls downhill until it hits something
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small MIRV
 Drops a group of large missiles on the ground from above
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Armour Piercing
 A small, powerful shell that knocks out tanks
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster Bomb
 Scatters medium yield bomblets on impact
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Super Cluster
 Scatters high yield bomblets on impact
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Funky Bomb
 Unpredictable and highly destructive
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Death
 Unpredictable and extremely destructive
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Bomblet
 Medium yield explosive warhead
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Funky Deathlet
 High yield explosive warhead
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Bomblet
 Medium yield explosive warhead
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Super Bomblet
 High yield explosive warhead
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Burrower
 Burrows back up to the surface before exploding, good against buried tanks. The Burrower is designed for below-ground use and suffers from high air-resistance.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Penetrator
 Penetrates below ground before rising to the surface. The Penetrator will either explode on exit from the ground or on contact with buried objects. This missile is designed for burrowing and as a result suffers from high air-resistance.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Napalm Bomb
 Scatters small quantities of intensely burning napalm on impact
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Medium Napalm Bomb
 Scatters intensely burning napalm on impact
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Large Napalm Bomb
 Covers the surrounding area with intensely burning napalm on impact
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Napalm Jelly
 Intensely burning chemical jelly
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Driller
 Vertical explosion that will do little damage, but create a deep hole in the ground.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tremor
 Produce a small earthquake on impact
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Shock Wave
 Produce a large earthquake on impact
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tectonic Shift
 Produce a huge earthquake on impact
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Riot Bomb
 Destroy a small volume of dirt without damaging anything else
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Heavy Riot Bomb
 Destroy a large volume of dirt without damaging anything else
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Riot Charge
 Cuts through a cone of dirt directly in front of the gun
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Riot Blast
 Cuts through a large cone of dirt directly in front of the gun
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Dirt Ball
 Produce a small sphere of material to bury your opponents
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Dirt Ball
 Produce a large sphere of material to bury your opponents
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Super Dirt Ball
 Produce a huge ball of material to bury your opponents
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Dirt Spread
 Drop little piles of dirt on your enemies
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster MIRV
 Releases a cluster of small missiles on its way down
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Per Cent Bomb
 Destroys half of the target's armour
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Reducer
 Lowers the explosive power of an enemy's missiles.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Laser
 A 50kW laser beam.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Laser
 A 100kW laser beam
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Large Laser
 A powerful 200kW laser beam
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Small Meteor
 A small chunk of rock from the skies
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Meteor
 A medium chunk of rock from the skies
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Meteor
 A large chunk of rock from the skies
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Small Lightning Bolt
 A weak bolt of lightning.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Lightning Bolt
 A bolt of lightning
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Lightning Bolt
 A powerful bolt of lightning
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teleport
 Teleports the tank to a random location
-5000 2 1 3 11
 
 Swapper
 Swap places with another tank
-10000 2 1 4 11
 
 Mass Teleport
 Teleports all tanks on the screen
-15000 2 1 4 11
 
 Fan
 Change the wind strength and direction
-2500 3 1 3 2
 
 Vengeance
 A small self-destruct or auto-destruct device, if you've got to go, why not take someone with you?
-20000 1 1 2 0 0 21
 
 Dying Wrath
 A large self/auto-destruct device. Mutually Assured Destruction.
-40000 1 1 3 0 21 3
 
 Fatal Fury
 End it all in style
-60000 1 1 4 0 22 3
 
 Light Shield
 A small amount of protection from damage
-10000 3 0 1 0 50 0 0 255 0 1
 
 Medium Shield
 Protects against  damage
-20000 2 0 2 0 100 0 64 255 0 3
 
 Heavy Shield
 A large amount of protection from damage
-30000 2 0 4 0 150 0 128 255 64 6
 
 Light Repulsor Shield
 Lightly repels enemy missiles
-10000 3 0 2 0 10 250 128 0 255 1
 
 Medium Repulsor Shield
 Repels enemy missiles
-20000 2 0 3 0 20 500 192 64 255 3
 
 Heavy Repulsor Shield
 Strongly repels enemy missiles
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Armour Plating
 Permanently add a small increase to the damage your tanks can take. Each additional purchase adds a slightly smaller amount to your tank's armour.
-20000 1 0 2 0 300
 
 Plasteel Plating
 Permanently increase the damage your tanks can take. Each additional purchase adds a slightly smaller amount to your tank's armour.
-40000 1 0 4 0 2155
 
 Intensity Amplifier
 A small permanent increase to the damage done by your weapons. The efficiency decreases and therefore each additional purchase has a reduced affect.
-21000 1 0 3 0 0.10
 
 Violent Force
 Permanently increase the damage done by your weapons. The efficiency decreases and as a result each additional purchase provides a smaller increase.
-50000 1 0 3 0 0.30
 
 Slick Projectiles
 A Teflon coating for projectiles to reduce drag and the affect of the wind
-1000 50 0 3 0 0.5
 
 Dimpled Projectiles
 Small dimples in the skin of projectiles for massive reduction in drag
-2000 50 0 4 0 0.1
 
 Parachute
 Allows the tank to float gently to the ground
-5000 10 0 0 0 0 0
 
 Auto-repair kit
 Repairs the tank a little each turn. Each additional kit provides a slightly smaller increase to your armour.
-10000 1 0 2 0 0 0
 
 Fuel
 Allows the tank to move across level terran.
-1000 10 0 2 0 0 0
 
 Rocket
 Launches the tank into the air.
-2000 2 1 4 0 0 0
 
 SDI Missile Defense
 Offers some protection against incoming missiles.
-10000 1 0 5 0 0
 
diff --git a/text/weapons.txt b/text/weapons.txt
index cb5b8eb..055a26c 100644
--- a/text/weapons.txt
+++ b/text/weapons.txt
@@ -1,277 +1,277 @@
 *WEAPONS*
 Small Missile
 Produces a low impact explosion
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+1500 10 10 0.2 25 0 2 30 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Missile
 Low yield, small area explosion
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+2000 5 15 0.2 40 1 2 40 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Missile
 Medium yield, medium area explosion
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+4000 3 20 0.2 60 2 3 60 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Small Nuke
 High yield, large area explosion
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
+16000 2 40 0.2 100 3 4 100 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Nuke
 Very high yield, huge explosion
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+22000 1 60 0.2 150 3 5 150 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Death Head
 Massive, deadly explosion
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+30000 1 80 0.2 200 4 6 200 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Spread
 5 Small missiles in a single shot
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+5000 3 10 0.2 25 0 2 30 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Spread
 5 Medium missiles in a single shot
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+11000 2 15 0.2 40 1 2 40 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Spread
 5 Large missiles in a single shot
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
+17000 1 20 0.2 60 2 3 60 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Super Spread
 4 Small Nukes in a single shot
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+25000 1 40 0.2 100 3 4 100 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Death Spread
 3 Nukes in a single shot
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+50000 1 60 0.2 150 3 5 150 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armageddon
 3 Death Heads in a single shot
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
+100000 1 80 0.2 200 4 6 200 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Chain Missile
 Fires a volly of three small missiles
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
+4000 12 10 0.2 25 0 2 30 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Chain Gun
 Fires a volly of five small missiles
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
+6000 15 10 0.2 25 0 2 30 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Jack Hammer
 Fires a volly of eight small missiles
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
+8000 16 10 0.2 25 0 2 30 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Shaped Charge
 High yield horizontal explosion. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
+10000 5 10 0.2 100 3 3 100 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Wide Boy
 Devastating horizontal explosion. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+15000 2 10 0.2 200 3 4 200 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cutter
 Horizontal explosion that will obliterate anything that gets in the way. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion in comparison to a similar sized uncontrolled blast.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
+20000 1 10 0.2 300 4 5 300 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Small Roller
 Medium explosive which rolls downhill until it hits something
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+2000 3 20 0.2 40 1 2 40 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Roller
 Large explosive which rolls downhill until it hits something
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
+8000 1 30 0.2 60 2 3 60 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Death Roller
 Huge explosive which rolls downhill until it hits something
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+14000 1 40 0.2 100 3 4 100 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small MIRV
 Drops a group of large missiles on the ground from above
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
+16000 2 200 0.2 250 2 2 300 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Armour Piercing
 A small, powerful shell that knocks out tanks
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
+2500 5 30 0.2 15 1 2 80 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster Bomb
 Scatters medium yield bomblets on impact
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
+10000 3 100 0.2 150 1 2 200 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Super Cluster
 Scatters high yield bomblets on impact
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
+20000 2 200 0.2 250 2 2 300 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Funky Bomb
 Unpredictable and highly destructive
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
+30000 3 50 0.2 200 2 0 200 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Death
 Unpredictable and extremely destructive
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
+50000 1 90 0.2 300 3 1 300 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Bomblet
 Medium yield explosive warhead
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
+7000 3 5 0.01 40 1 0 50 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Funky Deathlet
 High yield explosive warhead
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
+9000 2 9 0.01 100 3 1 90 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Bomblet
 Medium yield explosive warhead
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
+3500 3 20 0.2 40 1 2 40 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Super Bomblet
 High yield explosive warhead
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
+5000 2 40 0.2 60 2 2 65 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Burrower
 Burrows back up to the surface before exploding, good against buried tanks. The Burrower is designed for below-ground use and suffers from high air-resistance.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+7500 3 100 0.4 50 1 2 50 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Penetrator
 Penetrates below ground before rising to the surface. The Penetrator will either explode on exit from the ground or on contact with buried objects. This missile is designed for burrowing and as a result suffers from high air-resistance.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+20000 2 150 0.4 80 2 2 100 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Napalm Bomb
 Scatters small quantities of intensely burning napalm on impact
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
+6000 3 80 0.2 80 1 1 80 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Medium Napalm Bomb
 Scatters intensely burning napalm on impact
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
+14000 2 200 0.2 150 2 1 200 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Large Napalm Bomb
 Covers the surrounding area with intensely burning napalm on impact
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
+22000 1 400 0.2 200 3 2 500 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Napalm Jelly
 Intensely burning chemical jelly
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
+2000 5 5 0.5 10 20 10 10 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Driller
 Vertical explosion that will do little damage, but create a deep hole in the ground.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+5000 2 10 0.2 300 3 5 10 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tremor
 Produce a small earthquake on impact
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+3000 5 30 0.2 40 1 2 10 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Shock Wave
 Produce a large earthquake on impact
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+10000 2 60 0.2 100 2 3 20 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tectonic Shift
 Produce a huge earthquake on impact
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
+30000 1 100 0.2 150 3 4 30 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Riot Bomb
 Destroy a small volume of dirt without damaging anything else
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+2000 5 15 0.2 25 0 2 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Heavy Riot Bomb
 Destroy a large volume of dirt without damaging anything else
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+3000 2 30 0.2 100 1 2 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Riot Charge
 Cuts through a cone of dirt directly in front of the gun
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
+1000 5 15 0.2 50 0 2 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Riot Blast
 Cuts through a large cone of dirt directly in front of the gun
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
+2000 2 30 0.2 150 1 2 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Dirt Ball
 Produce a small sphere of material to bury your opponents
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
+3000 5 15 0.2 25 0 2 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Dirt Ball
 Produce a large sphere of material to bury your opponents
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+7000 2 30 0.2 60 1 2 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Super Dirt Ball
 Produce a huge ball of material to bury your opponents
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+10000 1 60 0.2 100 2 2 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Dirt Spread
 Drop little piles of dirt on your enemies
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+4000 2 30 0.2 40 1 2 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster MIRV
 Releases a cluster of small missiles on its way down
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
+10000 2 100 0.2 150 0 2 200 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Per Cent Bomb
 Destroys half of the target's armour
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
+12000 2 30 0.2 30 3 4 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Reducer
 Lowers the explosive power of an enemy's missiles.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+6000 3 25 0.3 30 3 4 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Laser
 A 50kW laser beam.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+5000 5 1 0.0 2 5 5 30 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Laser
 A 100kW laser beam
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+10000 3 1 0.0 5 5 5 65 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Large Laser
 A powerful 200kW laser beam
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
+15000 2 1 0.0 9 5 5 150 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Small Meteor
 A small chunk of rock from the skies
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+0 1 15 0.2 25 10 0 5 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Meteor
 A medium chunk of rock from the skies
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+0 1 20 0.2 40 11 0 10 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Meteor
 A large chunk of rock from the skies
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+0 1 25 0.2 60 12 1 20 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Small Lightning Bolt
 A weak bolt of lightning.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+0 1 1 0.0 1 30 0 5 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Lightning Bolt
 A bolt of lightning
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+0 1 1 0.0 4 30 0 15 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Lightning Bolt
 A powerful bolt of lightning
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
+0 1 1 0.0 7 31 0 35 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teleport
 Teleports the tank to a random location
-5000 2 1 3 11
+5000 2 1 3 6
 
 Swapper
 Swap places with another tank
-10000 2 1 4 11
+10000 2 1 4 6
 
 Mass Teleport
 Teleports all tanks on the screen
-15000 2 1 4 11
+15000 2 1 4 6
 
 Fan
 Change the wind strength and direction
-2500 3 1 3 2
+2500 3 1 3 7
 
 Vengeance
 A small self-destruct or auto-destruct device, if you've got to go, why not take someone with you?
-20000 1 1 2 0 0 21
+10000 1 1 2 2 1 21
 
 Dying Wrath
 A large self/auto-destruct device. Mutually Assured Destruction.
-40000 1 1 3 0 21 3
+25000 1 1 3 3 21 3
 
 Fatal Fury
 End it all in style
-60000 1 1 4 0 22 3
+50000 1 1 4 4 24 5
 
 Light Shield
 A small amount of protection from damage
@@ -339,5 +339,5 @@ Launches the tank into the air.
 
 SDI Missile Defense
 Offers some protection against incoming missiles.
-10000 1 0 5 0 0
+10000 1 0 5 5 0
 
diff --git a/text/weapons_ES.txt b/text/weapons_ES.txt
index a5c60f7..2e34a89 100644
--- a/text/weapons_ES.txt
+++ b/text/weapons_ES.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Misil peque�o
 Produce un bajo impacto de explosi�n
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Misil medio
 Bajo rendimiento, peque�a �rea de explosi�n
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Misil largo
 Rendimiento medio, �re media de explosi�n
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�a arma nuclear
 Alto rendimiento, �rea larga de explosi�n
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Arma nuclear
 Muy alto rendimiento, enorme explosi�n
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cabeza de muerte
 Masiva, explosi�n mortal
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�a propagaci�n
 5 Peque�os misiles en un �nico tiro
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Media propagaci�n
 5 Misiles medios en un �nico disparo
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Larga propagaci�n
 5 Largos misiles en un solo dispao
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Super propagaci�n
 4 Peque�as armas nucleares en un solo disparo
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Propagaci�n de muerte
 3 Armas nucleares en un solo disparo
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armagedon
 3 Cabezas de muerte en un solo disparo
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Cadena de misiles
 Disparos de tres peque�os misiles
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Ca�on de cadenas
 Dispara 5 peque�os misiles
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Jack Hammer
 Disparo de 8 peque�os misiles
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Carga formada
 Alto rendimiento de explosi�n horizontal. Toda la energ�a es enfocada fuera de los lados, aumentado el da�o hecho pero reduciendo el area total de explosi�n.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Chico amplio
 Explosi�n devastadora horizontal. Toda la energ�a es enfocada fuera de los lados, aumentado el da�o hecho pero reduciendo el area total de explosi�n.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cutter
 Explosi�n Horizontal que arrasar� con algo del camino. Toda la energ�a es enfocada fuera de los lados, aumentado el da�o hecho pero reduciendo el area total de explosi�n en comparaci�n de una descontrolada explosi�n.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Pequ�a apisonadora
 Explosi�n media cuando la rueda golpee algo
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Larga Apisonadora
 Larga explosi�n cuando la rueda golpee algo
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Apisonadora de muerte
 Enorme explosi�n cuando la rueda golpee algo
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�a MIRV
 Caida de un grupo de misiles largos desde el suelo
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Armadura penetrante
 Un peque�o, poderoso proyectil que noquea tanques
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Grupo de Bombas
 Medio rendimiento de impacto y dispersaci�n de Bombas
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Super Bombas
 Alto rendimiento de impacto y dispersaci�n de Bombas
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Bomba divertida
 Impredecible y altamente destructiva
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Muerte divertida
 Impredecible y extremadamente destructiva
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Divertido bombazo
 Rendimiento medio de explosi�n de ojiva
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Mortalmente Divertido
 Alto rendimiento de explosi�n de ojiva
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Bombazo
 Medio rendimiento de explosi�n de ojiva
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Super Bombazo
 Alto rendimiento de explosi�n de ojiva
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Burrower
 Burrows llegan a la superficie antes de explotar, bien contra tanques. El Burrower est� dise�ado para estar abajo y salir a la superficie con alta resistencia.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Penetrador
 Penetradores van abajo antes de ascender a la superficie. El Penetrador explotar� al salir y tener contacto con cualquier objeto.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�a Bomba  Napalm 
 Peque�as cantidades de dispersaci�n de Napales de intensamente impacto
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Media Bomba  Napalm 
 Quemadura intensa de impacto de Napalm
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Larga Bomba Napalm
 Cobertura circundante del area con intensamente ardimiento al impacto
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Pasta Napalm
 Ardimiento Intensamente qu�mico
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Taladror
 Explosi�n vertical que  hace un peque�o da�o, pero crea un profundo hoyo en el suelo.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Temblor
 Produce un peque�o terremoto al impacto
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Ola de impresi�n
 Produce un largo temblor de impacto
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cambio Tect�nico
 Produce un enorme temblor al impacto
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Boma Disturbios
 Destruye un peque�o volumen de suciedad sin perjudicar algo m�s
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Pesada Bomba de Disturbios
 Destruye un largo volumen de suciedad sin perjudicar algo m�s
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Carga de Disturbios
 Cortes a trav�s de un cono que va directamente al frente del arma
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Explosi�n de Disturbios
 Cortes a trav�s de un largo cono que va directamente al frente del arma
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Bola de suciedad
 PProduce una peque�a esfera de material que da�a a tus oponentes
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Larga Bola de Suciedad Dirt Ball
 Produce una larga esfera de material que da�a a tus oponentes 
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Super Bola de Suciedad
 Produce una enorme esfera de material que da�a a tus oponentes 
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�a propagaci�n de Suciedad
 Peque�a gota de suciedad en tus enemigos
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Grupo MIRV
 Lanzamiento de peque�os misiles en caidas
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Bomba Por Ciento
 Destruye la mitad de protecci�n de las armaduras
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Reductor
 Baja el poder de explosion de los misiles enemigos.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�o Laser
 Emite 50kW de laser.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Laser Medio
 Emite 100kW de laser
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Laser Largo
 Emite poderosos 200kW de laser
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Peque�o Meteoro
 Un peque�o trozo de roca de los cielos
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Meteoro Medio
 Un  trozo mediano de roca de los cielos
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Meteoro Largo
 Un  trozo largo de roca de los cielos
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Peque�o rayo rel�mpago
 Un d�bil rayo de rel�mpago.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medio rayo rel�mpago
 Un rayo de rel�mpago
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Largo  rayo rel�mpago 
 Un poderoso rayo de rel�mpago
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teletransportador
 Tele transporta el tanque a un lugar aleatorio
-5000 2 1 3 11
 
 Cambiador
 Cambia lugares con otro tanque
-10000 2 1 4 11
 
 Gran Teletransportador
 Tele transporta todos los tanques de la pantalla
-15000 2 1 4 11
 
 Ventilador
 Cambia la fuerza y direcci�n del viento
-2500 3 1 3 2
 
 Venganza
 Un peque�o dispositivo auto-destructor, si tienes que irte, por qu� no llevarte a alguien contigo?
-20000 1 1 2 0 0 21
 
 Ira Moribunda
 Un dispositivo largo de auto-destrucci�n. Destrucci�n Mutuamente Segura.
-40000 1 1 3 0 21 3
 
 Furia Fatal
 Acabalo todo con Estilo
-60000 1 1 4 0 22 3
 
 Luz Protectora
 Una peque�a cantidad de protecci�n de da�os
-10000 3 0 1 0 50 0 0 255 0 1
 
 Escudo Medio
 Protecci�n contra da�os
-20000 2 0 2 0 100 0 64 255 0 3
 
 Escudo Pesado
 Una larga cantidad de protecci�n de da�os 
-30000 2 0 4 0 150 0 128 255 64 6
 
 Escudo Repulsor de Luz
 Repele ligeramente los misiles enemigos
-10000 3 0 2 0 10 250 128 0 255 1
 
 Escudo Repulsor Medio
 Repele los misiles enemigos
-20000 2 0 3 0 20 500 192 64 255 3
 
 Pesado Escudo Repulsor
 Repele fuertemente los misiles enemigos
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Armadura Blindada
 Permanentemente a�ade un peque�o incremento al da�o que tus tanques pueden llevar. Cada compra adicional a�ade un peque�o monto a la armadura de tu tanque.
-20000 1 0 2 0 300
 
 Plasteel Plating
 Permanentemente a�ade un peque�o incremento al da�o que tus tanques pueden llevar. Cada compra adicional a�ade un peque�o monto a la armadura de tu tanque.
-40000 1 0 4 0 2155
 
 Amplifcador de Intensidad
 Un permanemte peque�o incremento al da�o hecho por tus armas. La eficiencia disminuye y por tanto cada compra adicional tiene un afecto reducido.
-21000 1 0 3 0 0.10
 
 Fuerza Violenta
 Un permanemte peque�o incremento al da�o hecho por tus armas. La eficiencia disminuye y como un resultado de compra adicional provee un peque�o incremento.
-50000 1 0 3 0 0.30
 
 Proyectiles Mejorados
 Una capa de Teflon para proyectiles que reduce el arrastre y afecta al viento
-1000 50 0 3 0 0.5
 
 Proyectiles Perforados
 Peque�os hoyuelos en la piel de los proyectiles para la reducci�n masica en el arrastre
-2000 50 0 4 0 0.1
 
 Paraca�das
 Permite al tanque flotar con delicadeza al suelo
-5000 10 0 0 0 0 0
 
 Kit de Auto-Reparaci�n
 Repara un poco el tanque en cada turno. Cada kit adicional provee un peque�o increment a tu armadura.
-10000 1 0 2 0 0 0
 
 Combustible
 Permite al tanque moverse en el terreno.
-1000 10 0 2 0 0 0
 
 Cohete
 Lanzamientos del tanque al aire.
-2000 2 1 4 0 0 0
 
 SDI Missile Defensa
 Ofrcece alguna protecci�n contra la llegada de misiles.
-10000 1 0 5 0 0
 
diff --git a/text/weapons_de.txt b/text/weapons_de.txt
index 7f98eb0..1a71db2 100644
--- a/text/weapons_de.txt
+++ b/text/weapons_de.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Kleine Rakete
 Kleine Explosion beim Einschlag.
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Mittlere Rakete
 Kleine Wirkung, kleinflächige Explosion.
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Große Rakete
 Mittlerer Wirkung, mittelflächige Explosion.
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Kleine Atombombe
 Große Wirkung, großflächige Explosion.
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Atombombe
 Sehr große Wirkung, riesige Explosion.
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Totenkopf
 Massive, tötliche Explosion.
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner Streuer
 5 Kleine Raketen mit einem Schuss.
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Mittlerer Streuer
 5 Mittlere Raketen mit einem Schuss.
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Großer Streuer
 5 Große Raketen mit einem Schuss.
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Super Streuer
 4 Kleine Atombomben mit einem Schuss.
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Todesstreuer
 3 Atombomben mit einem Schuss.
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armageddon
 3 Totenköpfe mit einem Schuss.
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Kleine Raktenkette
 Feuert drei kleine Rakten nacheinander ab.
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Raketenkette
 Feuert fünf kleine Rakten nacheinander ab.
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Jack Hammer
 Feuert acht kleine Rakten nacheinander ab.
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Shaped Charge
 Hoch wirksame horizontale Explosion. Die gesamte Energie ist zu den Seiten fokusiert und erhöht den Schaden und verdampft die gesamte Umgebung im Bereich der Explosion.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Wide Boy
 Verheerende horizontale Explosion. Die gesamte Energie ist zu den Seiten fokusiert und erhöht den Schaden und verdampft die gesamte Umgebung im Bereich der Explosion.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cutter
 Horizontale Explosion die alles auslöscht, was in ihren Weg kommt. Die gesamte Energie ist zu den Seiten fokusiert und erhöht den Schaden und verdampft die gesamte Umgebung im Bereich der Explosion, vergleichbar mit einer ebensogrßen unkontrollierten Sprengung.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner Roller
 Mittel explosiver Sprengsatz, der den Berg hinunter rollt bis er etwas trifft.
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Großer Roller
 Großer explosiver Sprengsatz, der den Berg hinunter rollt bis er etwas trifft.
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Todesroller
 Riesiger explosiver Sprengsatz, der den Berg hinunter rollt bis er etwas trifft.
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner MIRV
 Wirft eine Gruppe großer Raketen von oben auf das Zielgebiet.
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Rüstungsknacker
 Eine kleine, wirksame Granatedie die Panzer zerstören kann.
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster Bombe
 Streut einen mittelwirksamen Bombentepich, der beim Aufschlag explodiert.
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Super Cluster
 Streut einen hochwirksamen Bombentepich, der beim Aufschlag explodiert.
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Funky Bombe
 Unberechenbar und hoch zerstörerisch.
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Tod
 Unberechenbar und extrem zerstörerisch.
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Bombenteppich
 Mittel wirksamer explosiver Sprengkopf.
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Funky Todesteppich
 Hoch wirksamer explosiver Sprengkopf.
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Bombenteppich
 Mittel wirksamer explosiver Sprengkopf.
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Super Bombenteppich
 Hoch wirksamer explosiver Sprengkopf.
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Erdfresser
 Bohrt sich zurück zur Oberfläche bevor er explodiert. Gut gegen unterirdische Panzer. Der Erdfresser ist für unterirdische Einsätze entwickelt und leidet unter hohem Luftwiderstand.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Penetrator
 Bohrt sich unter die Oberfläche bevor er wieder zur Oberfläche kommt. The Penetrator explodiert entweder beim erreichen der Oberfläche oder bei Kontakt mit einem verschütteten Objekt. Diese Rakete ist für unterirdische Einsätze entwickelt und leidet unter hohem Luftwiderstand.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Kleine Napalmbombe
 Streut beim Einschlag kleine Mengen intensiv brennenden Napalms.
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Mittlere Napalmbombe
 Streut beim Einschlag intensiv brennendes Napalm.
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Große Napalmbombe
 Überzieht die Umgebung beim Einschlag mit intensiv brennendem Napalm.
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Napalmgel
 Intensiv brennendes chemisches Gel.
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Bohrer
 Vertikale Explosion, die kleinen Schaden anrichtet, aber ein tiefes Loch in die Oberfläche macht.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tremor
 Erzeugt beim Einschlag ein kleines Erdbeben.
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Schockwelle
 Erzeugt beim Einschlag ein großes Erdbeben.
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tektonische Bombe
 Erzeugt beim Einschlag ein riesiges Erdbeben.
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Rebellenbombe
 Zerstört eine kleine Menge Erde ohne etwas anderes zu zerstören.
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Große Rebellenbombe
 Zerstört eine große Menge Erde ohne etwas anderes zu zerstören.
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner Unruhestifter
 Schneidet einen Kegel aus dem Untergrund, direkt vor der Mündung.
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Großer Unruhestifter
 Schneidet einen großen Kegel aus dem Untergrund, direkt vor der Mündung.
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Dreckball
 Erzeugt einen kleinen Hügel um den Gegner zu verschütten.
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Großer Dreckball
 Erzeugt einen großen Hügel um den Gegner zu verschütten.
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Super Dreckball
 Erzeugt einen riesen Hügel um den Gegner zu verschütten.
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner Dreckball
 Drop little piles of dirt on your enemies
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster MIRV
 Wirft eine Gruppe kleiner Raketen von oben auf das Zielgebiet.
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Prozent Bombe
 Zerstört die Hälfte der gegnerischen Panzerung.
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Reduzierer
 Vermindert die Kraft gegnerischer Waffen.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner Laser
 Ein 50kW Laserstrahl.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Mittlerer Laser
 Ein 100kW Laserstrahl.
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Großer Laser
 Ein kraftvoller 200kW Laserstrahl.
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Kleiner Meteor
 Ein kleiner Felsbrocken vom Himmel.
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Mittlerer Meteor
 Ein mittlerer Felsbrocken vom Himmel.
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Großer Meteor
 Ein großer Felsbrocken vom Himmel.
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Kleiner Blitz
 Ein schwacher Blitz.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Mittlerer Blitz
 Ein Blitz.
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Großer Blitz - Kugelbllitz
 Ein starker Blitz.
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teleport
 Teleportiert den Panzer an einen zufälligen Ort.
-5000 2 1 3 11
 
 Tausch
 Tauscht mit einem anderen Panzer den Platz.
-10000 2 1 4 11
 
 Massenteleportation
 Teleportiert alle Panzer auf dem Bildschirm.
-15000 2 1 4 11
 
 Ventilator
 Ändert Windstärke und -richtung.
-2500 3 1 3 2
 
 Vengeance
 Ein kleiner Selbstzerstörer. Wenn Du gehen musst, warum nicht noch jemanden mitnehmen?
-20000 1 1 2 0 0 21
 
 Todeswut
 Ein großer Selbstzerstörer. Gleichgewicht des Schreckens.
-40000 1 1 3 0 21 3
 
 Tödliche Raserei
 Beendet jedes Laben.
-60000 1 1 4 0 22 3
 
 Schwaches Schild
 Ein kleiner Schutz.
-10000 3 0 1 0 50 0 0 255 0 1
 
 Mittleres Schild
 Schutz gegen Schaden.
-20000 2 0 2 0 100 0 64 255 0 3
 
 Schweres Schild
 EIn starker Schutz.
-30000 2 0 4 0 150 0 128 255 64 6
 
 Kleines Kraftfeld
 Lässt feindliche Raketen leicht abprallen.
-10000 3 0 2 0 10 250 128 0 255 1
 
 Mittleres Kraftfeld
 Lässt feindliche Raketen abprallen.
-20000 2 0 3 0 20 500 192 64 255 3
 
 Starkes Kraftfeld
 Lässt feindliche Raketen stark abprallen.
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Verstärkte Rüstung
 Erhöht die Treffer, die Dein Panzer einstecken kann ein wenig. Jeder weitere Kauf verstärkt die Rüstung Deines Panzers ein wenig.
-20000 1 0 2 0 300
 
 Plastahl Rüstung
 Erhöht die Treffer, die Dein Panzer einstecken kann ein wenig. Jeder weitere Kauf verstärkt die Rüstung Deines Panzers ein wenig.
-40000 1 0 4 0 2155
 
 Intensiver Verstärker
 Eine kleine permanente Erhöhung des Schadens, den Deine Waffe anrichtet. Mit jedem Kauf zeigt der Verstärker weniger Wirkung, so dass weiter Käufe weniger zusätzlichen Schaden bringen.
-21000 1 0 3 0 0.10
 
 Brutaler Verstärker
 Eine permanente Erhöhung des Schadens, den Deine Waffe anrichtet. Mit jedem Kauf zeigt der Verstärker weniger Wirkung, so dass weiter Käufe weniger zusätzlichen Schaden bringen.
-50000 1 0 3 0 0.30
 
 Glatte Projektile
 Eine Teflonbeschichtung reduziert den Luftwiderstand und den Effekt des Windes.
-1000 50 0 3 0 0.5
 
 Genoppte Projektile
 Kleine Noppen auf der Oberfläche der Projektile sorgen für eine massive Reduzierung des Luftwiderstandes.
-2000 50 0 4 0 0.1
 
 Fallschirm
 Erlaubt es dem Panzer sanft auf dem Boden zu landen.
-5000 10 0 0 0 0 0
 
 Reparatursatz
 Repariert den Panzer jede Runde ein wenig. Jeder weitere Reparatursatz unterstützt die Reparatur ein bisschen weniger.
-10000 1 0 2 0 0 0
 
 Benzin
 Erlaubt es dem Panzer, sich auf ebenem Gelände zu bewegen.
-1000 10 0 2 0 0 0
 
 Rakete
 Schießt den Panzer in die Luft.
-2000 2 1 4 0 0 0
 
 SDI Missile Defense System
 Bietet Schutz gegen ankommende Geschosse.
-10000 1 0 5 0 0
 
diff --git a/text/weapons_fr.txt b/text/weapons_fr.txt
index cb5b8eb..bfdc440 100644
--- a/text/weapons_fr.txt
+++ b/text/weapons_fr.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Small Missile
 Produces a low impact explosion
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Missile
 Low yield, small area explosion
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Missile
 Medium yield, medium area explosion
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Small Nuke
 High yield, large area explosion
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Nuke
 Very high yield, huge explosion
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Death Head
 Massive, deadly explosion
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Spread
 5 Small missiles in a single shot
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Spread
 5 Medium missiles in a single shot
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Spread
 5 Large missiles in a single shot
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Super Spread
 4 Small Nukes in a single shot
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Death Spread
 3 Nukes in a single shot
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armageddon
 3 Death Heads in a single shot
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Chain Missile
 Fires a volly of three small missiles
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Chain Gun
 Fires a volly of five small missiles
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Jack Hammer
 Fires a volly of eight small missiles
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Shaped Charge
 High yield horizontal explosion. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Wide Boy
 Devastating horizontal explosion. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cutter
 Horizontal explosion that will obliterate anything that gets in the way. All of the energy is focused out to the sides, increasing the damage done but reducing the overall area of the explosion in comparison to a similar sized uncontrolled blast.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Small Roller
 Medium explosive which rolls downhill until it hits something
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Roller
 Large explosive which rolls downhill until it hits something
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Death Roller
 Huge explosive which rolls downhill until it hits something
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small MIRV
 Drops a group of large missiles on the ground from above
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Armour Piercing
 A small, powerful shell that knocks out tanks
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster Bomb
 Scatters medium yield bomblets on impact
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Super Cluster
 Scatters high yield bomblets on impact
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Funky Bomb
 Unpredictable and highly destructive
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Death
 Unpredictable and extremely destructive
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Bomblet
 Medium yield explosive warhead
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Funky Deathlet
 High yield explosive warhead
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Bomblet
 Medium yield explosive warhead
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Super Bomblet
 High yield explosive warhead
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Burrower
 Burrows back up to the surface before exploding, good against buried tanks. The Burrower is designed for below-ground use and suffers from high air-resistance.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Penetrator
 Penetrates below ground before rising to the surface. The Penetrator will either explode on exit from the ground or on contact with buried objects. This missile is designed for burrowing and as a result suffers from high air-resistance.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Napalm Bomb
 Scatters small quantities of intensely burning napalm on impact
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Medium Napalm Bomb
 Scatters intensely burning napalm on impact
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Large Napalm Bomb
 Covers the surrounding area with intensely burning napalm on impact
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Napalm Jelly
 Intensely burning chemical jelly
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Driller
 Vertical explosion that will do little damage, but create a deep hole in the ground.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tremor
 Produce a small earthquake on impact
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Shock Wave
 Produce a large earthquake on impact
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tectonic Shift
 Produce a huge earthquake on impact
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Riot Bomb
 Destroy a small volume of dirt without damaging anything else
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Heavy Riot Bomb
 Destroy a large volume of dirt without damaging anything else
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Riot Charge
 Cuts through a cone of dirt directly in front of the gun
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Riot Blast
 Cuts through a large cone of dirt directly in front of the gun
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Dirt Ball
 Produce a small sphere of material to bury your opponents
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Large Dirt Ball
 Produce a large sphere of material to bury your opponents
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Super Dirt Ball
 Produce a huge ball of material to bury your opponents
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Dirt Spread
 Drop little piles of dirt on your enemies
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster MIRV
 Releases a cluster of small missiles on its way down
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Per Cent Bomb
 Destroys half of the target's armour
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Reducer
 Lowers the explosive power of an enemy's missiles.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Laser
 A 50kW laser beam.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Laser
 A 100kW laser beam
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Large Laser
 A powerful 200kW laser beam
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Small Meteor
 A small chunk of rock from the skies
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Meteor
 A medium chunk of rock from the skies
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Meteor
 A large chunk of rock from the skies
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Small Lightning Bolt
 A weak bolt of lightning.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Medium Lightning Bolt
 A bolt of lightning
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Large Lightning Bolt
 A powerful bolt of lightning
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teleport
 Teleports the tank to a random location
-5000 2 1 3 11
 
 Swapper
 Swap places with another tank
-10000 2 1 4 11
 
 Mass Teleport
 Teleports all tanks on the screen
-15000 2 1 4 11
 
 Fan
 Change the wind strength and direction
-2500 3 1 3 2
 
 Vengeance
 A small self-destruct or auto-destruct device, if you've got to go, why not take someone with you?
-20000 1 1 2 0 0 21
 
 Dying Wrath
 A large self/auto-destruct device. Mutually Assured Destruction.
-40000 1 1 3 0 21 3
 
 Fatal Fury
 End it all in style
-60000 1 1 4 0 22 3
 
 Light Shield
 A small amount of protection from damage
-10000 3 0 1 0 50 0 0 255 0 1
 
 Medium Shield
 Protects against  damage
-20000 2 0 2 0 100 0 64 255 0 3
 
 Heavy Shield
 A large amount of protection from damage
-30000 2 0 4 0 150 0 128 255 64 6
 
 Light Repulsor Shield
 Lightly repels enemy missiles
-10000 3 0 2 0 10 250 128 0 255 1
 
 Medium Repulsor Shield
 Repels enemy missiles
-20000 2 0 3 0 20 500 192 64 255 3
 
 Heavy Repulsor Shield
 Strongly repels enemy missiles
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Armour Plating
 Permanently add a small increase to the damage your tanks can take. Each additional purchase adds a slightly smaller amount to your tank's armour.
-20000 1 0 2 0 300
 
 Plasteel Plating
 Permanently increase the damage your tanks can take. Each additional purchase adds a slightly smaller amount to your tank's armour.
-40000 1 0 4 0 2155
 
 Intensity Amplifier
 A small permanent increase to the damage done by your weapons. The efficiency decreases and therefore each additional purchase has a reduced affect.
-21000 1 0 3 0 0.10
 
 Violent Force
 Permanently increase the damage done by your weapons. The efficiency decreases and as a result each additional purchase provides a smaller increase.
-50000 1 0 3 0 0.30
 
 Slick Projectiles
 A Teflon coating for projectiles to reduce drag and the affect of the wind
-1000 50 0 3 0 0.5
 
 Dimpled Projectiles
 Small dimples in the skin of projectiles for massive reduction in drag
-2000 50 0 4 0 0.1
 
 Parachute
 Allows the tank to float gently to the ground
-5000 10 0 0 0 0 0
 
 Auto-repair kit
 Repairs the tank a little each turn. Each additional kit provides a slightly smaller increase to your armour.
-10000 1 0 2 0 0 0
 
 Fuel
 Allows the tank to move across level terran.
-1000 10 0 2 0 0 0
 
 Rocket
 Launches the tank into the air.
-2000 2 1 4 0 0 0
 
 SDI Missile Defense
 Offers some protection against incoming missiles.
-10000 1 0 5 0 0
 
diff --git a/text/weapons_it.txt b/text/weapons_it.txt
index 50356a1..71df070 100644
--- a/text/weapons_it.txt
+++ b/text/weapons_it.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Missile Piccolo
 Produce una piccola esplosione all'impatto
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Missile Medio
 Basso rendimento, piccola area esplosione
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Missile Grande
 Medio rendimento, media area esplosione
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Bomba Nucleare Piccola
 Alto rendimento, larga area esplosione
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Bomba Nucleare
 Altissimo rendimento, enorme area esplosione
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Testata della Morte
 Massivo, esplosione mortale
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Piccola Diffusione
 5 Piccoli missili in un sigolo colpo
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Media Diffusione
 5 Medio missili in un sigolo colpo
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Grande Diffusione
 5 Grandi missili in un sigolo colpo
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Super Diffusione
 4 Piccole Bombe Nucleari in un sigolo colpo
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Mortale Diffusione
 3 Bombe Nucleari in un sigolo colpo
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armageddon
 3 Testate della Morte in un sigolo colpo
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Missile Concatenato
 Fuoco con un colpo di tre piccoli missili
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Cannone Concatenato
 Fuoco con un colpo di cinque piccoli missili
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Jack Hammer
 Fuoco con un colpo di otto piccoli missili
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Shaped Charge
 Esplosione orizzontale ad alto rendimento. Tutta l'energia e' focalizzata sui lati, incrementando la distruzione, ma riducendo l'area dell'esplosione.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Wide Boy
 Esplosione orizzontale devastante. Tutta l'energia e' focalizzata sui lati, incrementando la distruzione, ma riducendo l'area dell'esplosione.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cutter
 Esplosione orizzontale che cancellera' tutto quello che trovera' per la strada. Tutta l'energia e' focalizzata sui lati, incrementando la distruzione, ma riducendo l'area dell'esplosione similarmente a quanto ottenuto con uno scoppio incontrollato.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Roller Piccolo
 Esplosione Media con dispositivo rotolante che esplodera' quando tocchera' qualcosa.
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Roller Grande
 Esplosione Grande con dispositivo rotolante che esplodera' quando tocchera' qualcosa.
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Roller della Morte
 Esplosione Enorme con dispositivo rotolante che esplodera' quando tocchera' qualcosa.
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 MIRV Piccolo
 Caduta un gruppo di grandi missili.
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Perforatore di Corazza
 Un piccolo, potentissimo perforatore di corazza dei tank
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Bomba Cluster
 Spargimenti a Medio rendimento di mazzi di bombe a impatto
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Super Cluster
 Spargimenti ad Alto rendimento di mazzi di bombe a impatto
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Bomba Funky
 Imprevedibile ed altamente distruttivo
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Funky Mortale
 Imprevedibile ed estremamente distruttivo
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Proietto Funky
 Testata esplosiva a medio rendimento
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Funky Quasi Mortale
 Testata esplosiva ad alto rendimento
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Proietto
 Testata esplosiva a medio rendimento
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Super Proietto
 Testata esplosiva ad alto rendimento
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Scavatore
 Scava nel terreno fino alla superficie prima dell'esplosione, buono contro i carri armati sepolti. Lo Scavatore � progettato per uso sotterraneo e soffre di conseguenza all'esposiizone all'aria.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Penetratore
 Penetra sepolto prima di sorgere alla superficie. Il Penetratore esploder� sia all'uscita dal suolo, sia al contatto con gli oggetti sepolti. Questo missile � disegnato per scavare e soffre di conseguenza all'esposiizone all'aria.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Piccola Bomba al Napalm
 Copre l'area circostante con piccole quantita' di napalm intensamente in fiamme dopo l'impatto
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Media Bomba al Napalm
 Copre l'area circostante con napalm intensamente in fiamme dopo l'impatto
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Grande Bomba al Napalm
 Copre l'area circostante con grandi quantita' di napalm intensamente in fiamme dopo l'impatto
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Napalm Gelatina
 Gelatina chimica intensamente combustibile
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Perforatrice
 Esplosione Verticale che provoca un piccolo danno, ma crea un profondo buco nel terreno.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tremor
 Produce un piccolo terremoto all'impatto
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Onda d'urto
 Produce un grande terremoto all'impatto
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Spostamento tettonico
 Produce un enorme terremoto all'impatto
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Bomba Anti-Sommossa
 Distrugge un piccolo volume di sporcizia senza danneggiare nulla
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Pesante Bomba Anti-Sommossa
 Distrugge un grande volume di sporcizia senza danneggiare nulla
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Multi Bomba Anti-Sommossa
 Taglia un cono di sporcizia direttamente davanti al cannone
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Bomba Anti-Sommossa Esplodente
 Taglia un grande cono di sporcizia direttamente davanti al cannone
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Palla Sporca
 Produce una piccola sfera di materiale per seppellire i vostri avversari
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Grande Palla Sporca
 Produce una grande sfera di materiale per seppellire i vostri avversari
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Super Palla Sporca
 Produce una enorme sfera di materiale per seppellire i vostri avversari
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Sporcizia a Piccola Diffusione
 Fa cadere dei piccoli mucchi di sporcizia sui vostri nemici
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster MIRV
 Libera un mazzo di piccoli missili su quello che trova
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Bomba Percentuale
 Distrugge meta' della corazza dell'obiettivo
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Riduttore
 Abbassa la potenza esplosiva dei missili nemici.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Laser Piccolo
 Raggio Laser a 50kW.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Laser Medio
 Raggio Laser a 100kW.
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Laser Grande
 Raggio Laser a 200kW.
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Meteorite Piccolo
 Un piccolo pezzo di roccia dallo spazio
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Meteorite Medio
 Un pezzo medio di roccia dallo spazio
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Meteorite Grande
 Un grande pezzo di roccia dallo spazio
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Lampo Piccolo
 Un piccolo fulmine dal cielo.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Lampo Medio
 Un fulmine medio dal cielo.
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Lampo Grande
 Un gran fulmine dal cielo.
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teletrasporto
 Teletrasporta il tank in una posizione random.
-5000 2 1 3 11
 
 Swapper
 Scambia il posto con un altro tank
-10000 2 1 4 11
 
 Teletrasporto Massiccio
 Teletrasporta tutti i tank sullo schermo.
-15000 2 1 4 11
 
 Fan
 Cambia il vento in forza e direzione
-2500 3 1 3 2
 
 Vendetta
 Un piccolo dispositivo di auto-distruzione, se avete deciso di andarvene, perche' non prenderne qualcuno con voi?
-20000 1 1 2 0 0 21
 
 Rabbia di Morte
 Un gran dispositivo di auto-distruzione. Distruzione reciprocamente assicurata.
-40000 1 1 3 0 21 3
 
 Furia Fatale
 Fa finire tutto in grande stile
-60000 1 1 4 0 22 3
 
 Scudo Leggero
 Da una piccola protezione dalla distruzione
-10000 3 0 1 0 50 0 0 255 0 1
 
 Scudo Medio
 Protegge dalla distruzione
-20000 2 0 2 0 100 0 64 255 0 3
 
 Scudo Pesante
 Da una grande protezione dalla distruzione
-30000 2 0 4 0 150 0 128 255 64 6
 
 Schermo di Repulsione Leggero
 Respinge leggermente i missili nemici
-10000 3 0 2 0 10 250 128 0 255 1
 
 Schermo di Repulsione Medio
 Respinge i missili nemici
-20000 2 0 3 0 20 500 192 64 255 3
 
 Schermo di Repulsione Pesante
 Respinge pesantemente i missili nemici
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Rinforzo Corazzatura
 Aggiunge permanentemente un piccolo aumento di resistenza alla corazza del tank diminuendo cosi' il danno inferto ai vostri tank.
-20000 1 0 2 0 300
 
 Rinforzo Corazzatura in Acciaio
 Aggiunge permanentemente un aumento di resistenza alla corazza del tank diminuendo cosi' il danno inferto ai vostri tank.
-40000 1 0 4 0 2155
 
 Amplificatore di Intensita'
 Permette un piccolo e permanente incremento ai danni fatti dalle vostre armi. L'efficienza diminuisce e quindi ogni acquisto supplementare ha un'influenza riduttrice.
-21000 1 0 3 0 0.10
 
 Forza Violenta
 Permette un permanente incremento ai danni fatti dalle vostre armi. L'efficienza diminuisce e quindi ogni acquisto supplementare ha un'influenza riduttrice.
-50000 1 0 3 0 0.30
 
 Proiettili Lucidi
 Un rivestimento di Teflon riduce la resistenza e l'influenza del vento sui vostri proiettili
-1000 50 0 3 0 0.5
 
 Proiettili Scanalati
 Piccole fossette nella superficie dei proiettili per una riduzione voluminosa della resistenza all'aria
-2000 50 0 4 0 0.1
 
 Paracadute
 Permette al tank di fluttuare lentamente a terra
-5000 10 0 0 0 0 0
 
 Kit Auto-Riparazione
 Ripara il tank un poco ogni volta. Ogni volta fornisce un piccolo aumento alla vostra corazza.
-10000 1 0 2 0 0 0
 
 Benzina
 Permette al tank di muoversi sul terreno.
-1000 10 0 2 0 0 0
 
 Razzo
 Solleva il tank nell'aria.
-2000 2 1 4 0 0 0
 
 Difesa anti missile - SDI
 Offre una certa protezione contro i missili ricevuti.
-10000 1 0 5 0 0
 
diff --git a/text/weapons_ru.txt b/text/weapons_ru.txt
index f265e23..088e582 100644
--- a/text/weapons_ru.txt
+++ b/text/weapons_ru.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Ракета
 Слабый взрыв, малая мощность. Достаточно для того, чтобы добить танк противника.
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Средняя ракета
 Небольшая мощность, небольшая площадь поражения. Разумное качество по разумной цене.
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Большая ракета
 Средняя мощность, средняя площадь поражения. Способна серьёзно задеть противника.
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Малая ядерная бомба
 Мощный взрыв, большая площадь поражения. Неприятный сюрприз для большинства танков.
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Ядерная бомба
 Очень мощный взрыв, огромная площадь поражения. Кто там не надел очки?
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Ядрен-батон (Death Head)
 Офигенно мощный бабах. Достанет кого угодно.
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Малый веер
 5 малых ракет за один выстрел. Удобно, если нужно накрыть нескольких раненых противников за раз.
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Средний веер
 5 средних ракет за один выстрел. Уже повод для беспокойства.
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Большой веер
 5 больших ракет за один выстрел. Опасное оружие в руках умелого танкиста.
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Супер-веер
 4 малых ядерных бомбы за один выстрел. Смех злого гения прилагается.
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Смертельный веер
 3 ядерных бомбы за один выстрел. Не забудьте надеть очки.
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Армагеддон
 3 ядрен-батона за один выстрел. Гарантированное избавление от тараканов и клопов на поле боя!
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Очередь
 Выстреливает 3 ракеты одну за другой. Для тех, кто хочет пощекотать противнику нервы.
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Пулемет
 Выстреливает 5 ракет одну за другой. Способен продолбить яму в грунте.
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Молотилка
 Выстреливает 8 ракет одну за другой. Начинающий набор танкиста-дятла.
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Резачок
 Мощный горизонтальный взрыв. Вся энергия фокусируется по сторонам, что увеличивает мощность, но уменьшает площадь поражения.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Резак
 Опустошительный горизонтальный взрыв. Вся энергия фокусируется по сторонам, что позволяет пробить любой щит, но уменьшает шансы зацепить врага.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Потрошитель
 Горизонтальный взрыв, сметающий всё на своем пути. Вся энергия фокусируется по сторонам, прорезая любую броню, бетон и грунт.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Каток
 Заряд средней мощности, который катится под горку, пока не столкнется с препятствием. Полезен против противника, который расположился на склоне.
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Большой каток
 Заряд большой мощности, который катится под горку, пока не столкнется с препятствием. Многие танкисты предпочитают встречу с лавиной.
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Танкоукладчик
 Заряд огромной мощности, способный укатать любой танк в прямом смысле этого слова.
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 РГЧ-ИН "Рогач"
 Ракета с Разделяющейся Головной Частью. Обрушивает с высоты на наземную цель группу больших ракет. Превращает точку попадания в подобие лунной поверхности.
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Бронебойный снаряд
 Небольшой, но очень мощный снаряд - сила взрыва концентрируется в одной точке, что позволяет пробить любую защиту.
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Кассетная бомба
 При срабатывании выбрасывает 5 средних ракет. Эффективна против врагов, собравшихся в группы.
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Супер-кассетная бомба
 При срабатывании выбрасывает 5 больших ракет. Разрушительный фейерверк в тылу врага.
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Шариковая бомба
 Экспериментальная бомба, сделанная на заводе по производству шариков для пинг-понга. Непредсказуема и смертельна.
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Шариковая смерть
 Улучшенная версия шариковой бомбы, сделанная на заводе по производству баскетбольных мячей. Непредсказуема и офигенно разрушительна.
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Воздушный шарик
 Летающая боеголовка средней мощности.
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Бомбаэростат
 Летающая боеголовка большой мощности.
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Бомбочка
 Боеголовка средней мощности. Для использования в кассетных бомбах, авиаударах и фейерверках.
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Супер-бомбочка
 Боеголовка большой мощности. Руками не трогать.
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Крот
 Вонзается в поверхность и проходит некоторый путь под землёй по параболе, прежде чем взорваться, что удобно против закопанных танков. Крот предназначен для движения под землей и не слишком-то хорошо летает в воздухе.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Большой крот
 Движется под землей до выхода на поверхность или до попадания в зарытый танк. Большой крот расчитан на движение под землей, поэтому страдает от сильного сопротивления воздуха.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Малый напалм
 Снаряд с горючими веществами. При срабатывании разбрызгивает немного горящего напалма. Напалм почти не разрушает грунт, но подвержен влиянию ветра.
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Средний напалм
 Снаряд с горючими веществами. Разбрызгивает горящий напалм при срабатывании. Более мощная и более эффективная модификация.
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Большой напалм
 Снаряд с горючими веществами. Заливает напалмом всё в округе. Проверьте направление ветра перед выстрелом.
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Липкий напалм
 Горючее искуственное желе.
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Ямокопатель
 При попадании снаряд создает глубокий вертикальный тоннель, почти не нанося повреждений. Первоначально использовался компаниями при поиске нефти и газа на планетах.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Дрожь земли
 При срабатывании вызывает слабое землетрясение. Малоэффективный, но назойливый снаряд.
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Ударная волна
 Вызывает большое землетрясение. Опасен для неподготовленных танков.
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Тектонический сдвиг
 Вызывает кошмарное землетрясение. Не кантовать.
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Уборщик (Riot Bomb)
 Убирает немного грунта, не повреждая броню и технику. Используется для раскопок на поле боевых действий.
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Heavy Riot Bomb
 Убирает уйму грунта, не повреждая броню и технику. Используется для обнаружения подземных вражеских лагерей.
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Малый дворник
 Очищает от грязи небольшой просвет перед дулом пушки. Предмет первой необходимости на поле боя.
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Большой дворник
 Очищает от грязи большую площадь перед дулом пушки. Улучшенная и в разы более эффективная модификация дворника.
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Комок грязи
 Небольшой шар из грунта, способный закопать оппонента. Грязь легко уничтожается и расчищается.
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Большой комок грязи
 Большой грунтовый шар для закапывания оппонентов. Полезен против тех, кто умеет откапывать танки только ракетами.
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Говномет
 Уйма грязи на голову врага. Для полноты ощущений ещё и воняет.
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Small Dirt Spread
 Drop little piles of dirt on your enemies
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Cluster MIRV
 Releases a cluster of small missiles on its way down
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Per Cent Bomb
 Destroys half of the target's armour
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Reducer
 Lowers the explosive power of an enemy's missiles.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Малый лазер
 Луч мощностью 50КВт. Способен прожечь лист железа или грунт.
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Средний лазер
 Луч мощностью 100КВт. Отличный способ прожечь броню врага.
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Большой лазер
 200-киловаттный прожигатель. Злые гении, это ваш выбор!
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Метеорит
 Камешек с небес. Лучше его не ловить.
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Метеор
 Осколок небесной тверди. Несовместим с целым танком.
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Большой метеор
 Обломок небесной тверди. Гнев небес вобьет в грунт любого грешника.
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Малая молния
 Слабенький разряд. Мешает радиосвязи, добивает больных и ослабевших.
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Молния
 Обычный разряд молнии. Выжигает бортовую электронику. Не лови - убьет!
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Большая молния
 Убийственный разряд. Кто сказал, что бывают тугоплавкие металлы?
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Телепорт
 Перебрасывает танк в случайную точку под землёй или в воздухе.
-5000 2 1 3 11
 
 Обменник
 Меняет танк местами с другим танком.
-10000 2 1 4 11
 
 Миксер
 Телепортирует в случайные точки все танки на поле боя.
-15000 2 1 4 11
 
 Ветродуй
 Меняет направление и силу ветра.
-2500 3 1 3 2
 
 Завещание
 Небольшой самоликвидатор или автоликвидатор: если уж тебя попросили на выход, почему бы не прихватить кого-нибудь с собой?
-20000 1 1 2 0 0 21
 
 Месть покойника
 Большой набор самоликвидации. Ты мне - я тебе.
-40000 1 1 3 0 21 3
 
 Убийственная ярость
 Закончим всё это красиво.
-60000 1 1 4 0 22 3
 
 Малый щит
 Противопульная дополнительная броня. Для тех, кому нужна дополнительная защита.
-10000 3 0 1 0 50 0 0 255 0 1
 
 Средний щит
 Хорошая дополнительная броня. Нужна защита от мощных ракет? Возьми этот модуль!
-20000 2 0 2 0 100 0 64 255 0 3
 
 Тяжелый щит
 Особо прочная дополнительная броня. Способна выдержать даже авиаудар.
-30000 2 0 4 0 150 0 128 255 64 6
 
 Слабое силовое поле
 Отталкивает вражеские снаряды. Не защищает от взрывной волны.
-10000 3 0 2 0 10 250 128 0 255 1
 
 Силовое поле
 Хорошо отталкивает вражеские снаряды, плохо защищает от взрывов.
-20000 2 0 3 0 20 500 192 64 255 3
 
 Мощное силовое поле
 Отлично отталкивает вражеские снаряды, направленные точно в танк.
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Навесная броня
 Усиливает броню танка до конца игры. Каждая новая пластина дает чуть меньший эффект, нежели предыдущая.
-20000 1 0 2 0 300
 
 Эгида
 Особо прочная навесная броня. Усиливает броню танка до конца игры. Каждая новая пластина дает чуть меньший эффект, нежели предыдущая.
-40000 1 0 4 0 2155
 
 Усилитель боевой мощи
 До конца игры немного увеличивает убойную силу ваших снарядов. Каждый новый усилитель оказывает немного меньший эффект по сравнению с предыдущим.
-21000 1 0 3 0 0.10
 
 Убийственный усилитель
 До конца игры существенно увеличивает убойную силу ваших снарядов. Каждый новый усилитель оказывает немного меньший эффект по сравнению с предыдущим.
-50000 1 0 3 0 0.30
 
 Тефлоновое покрытие
 Тефлоновая оболочка зарядов уменьшает влияние ветра на траекторию выстрела.
-1000 50 0 3 0 0.5
 
 Сверлёные снаряды
 Небольшие отверстия в боеголовках существенно повышают их устойчивость к воздействию ветра.
-2000 50 0 4 0 0.1
 
 Парашют
 Оказавшись в воздухе или потеряв почву под ногами, танк не падает, а плавно опускается на землю. В полете можно управлять направлением, расходуя ТОПЛИВО, если оно есть.
-5000 10 0 0 0 0 0
 
 Авторемонтник
 С каждым ходом устраняет часть полученных повреждений. Каждый новый ремонтник работает чуть менее эффективно, чем предыдущий.
-10000 1 0 2 0 0 0
 
 Топливо
 Позволяет танку двигаться по ровной местности и планировать в сторону при парашютировании.
-1000 10 0 2 0 0 0
 
 Двигатель "ВВП"
 Поднимает танк в воздух, что позволяет перелетать с места на место.
-2000 2 1 4 0 0 0
 
 SDI Missile Defense
 Offers some protection against incoming missiles.
-10000 1 0 5 0 0
 
diff --git a/text/weapons_sk.txt b/text/weapons_sk.txt
index e854bd0..e175385 100644
--- a/text/weapons_sk.txt
+++ b/text/weapons_sk.txt
@@ -1,343 +1,258 @@
 *WEAPONS*
 Malá strela
 Pri kontakte spôsobí malú explóziu
-1500 10 10 0.2 25 0 2 30 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Stredná strela
 Malá ráž, malá veľkosť explózie
-2000 5 15 0.2 40 0 2 40 0 1 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Veľká strela
 Stredná ráž, stredná veľkosť explózie
-4000 3 20 0.2 60 1 3 60 0 2 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Malá jadrová bomba
 Veľká ráž, veľká explózia
-16000 2 40 0.2 100 2 4 100 0 3 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Jadrová bomba
 Veľmi veľká ráž, obrovská explózia
-22000 1 60 0.2 150 2 5 150 0 4 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Vraždiaca bomba
 Obrovská smrtiaca explózia
-30000 1 80 0.2 200 3 6 200 0 5 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Ľahké kobercové bombardovanie
 5 malý striel v jednom výstrele
-5000 3 10 0.2 25 0 2 30 0 0 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Stredné kobercové bombardovanie
 5 stredných striel v jednom výstrele
-11000 2 15 0.2 40 0 2 40 0 1 5 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Ťažké kobercové bombardovanie
 5 veľkých striel v jednom výstrele
-17000 1 20 0.2 60 1 3 60 0 2 5 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Superkobercové bombardovanie
 4 malé jadrové bomby v jednom výstrele
-25000 1 40 0.2 100 2 4 100 0 3 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Smrtiace kobercové bombardovanie
 3 jadrové bomby v jednom výstrele
-50000 1 60 0.2 150 2 5 150 0 4 3 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Armagedon
 3 smrtiace hlavice v jednom výstrele
-100000 1 80 0.2 200 3 6 200 0 5 3 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Reťazové strely
 Vypáli skupinku troch malých striel
-4000 3 10 0.2 25 0 2 30 0 0 1 3 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Samopal
 Vypáli skupinku piatich malých striel
-6000 10 10 0.2 25 0 2 30 0 0 1 5 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Pneumatické kladivo
 Vypáli skupinku ôsmych malých striel
-8000 16 10 0.2 25 0 2 30 0 0 1 8 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Kumulatívna nálož
 Veľká horizontálna explózia. Všetka energia je rozprestrená do strán, čím sa zvyšuje škoda, no zmenšuje oblasť, na ktorú explózia pôsobí.
-10000 5 10 0.2 100 2 3 100 0 17 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Wide Boy
 Zničujúca horizontálna explózia. Všetka energia je rozprestrená do strán, čím sa zvyšuje škoda, no zmenšuje oblasť, na ktorú explózia pôsobí.
-15000 2 10 0.2 200 2 4 200 0 18 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Orezávač
 Horizontálna explózia, ktorá vyhladí všetko, čo jej príde do cesty. Všetka energia je rozprestrená do strán, čím sa zvyšuje škoda, no zmenšuje oblasť, na ktorú explózia pôsobí v porovnaní s neriadenou detonáciou podobnej sily.
-20000 1 10 0.2 300 3 5 300 0 19 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Malý valcovač
 Stredná trhavina, ktorá sa valí z kopca až kým do niečo nenarazí
-2000 3 20 0.2 40 0 2 40 0 6 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Veľký valcovač
 Silná trhavina, ktorá sa valí z kopca až kým do niečo nenarazí
-8000 1 30 0.2 60 1 3 60 0 6 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Smrtiaci valcovač
 Veľmi silná trhavina, ktorá sa valí z kopca až kým do niečo nenarazí
-14000 1 40 0.2 100 2 4 100 0 6 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Malá MIRV
 Spustí z neba skupinu veľkých striel
-20000 1 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 2 1.0 120 0.0 1.0 0 -1 0
 
 Trhač brnenia
 Malý silný náboj, ktorý dokáže zničiť tank
-2500 5 30 0.2 15 0 2 80 0 27 1 0 0 2 0 0 -1 0 0 0 0 0 -1 0
 
 Trieštivá bomba
 Rozhodí bombičky o strednej ráži, ktoré vybuchujú pri kontakte
-10000 3 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 60 0 3.5 0 -1 0
 
 Supertrieštivá bomba
 Rozhodí bombičky o veľkej ráži, ktoré vybuchujú pri kontakte
-20000 2 200 0.2 250 1 2 300 0 7 1 0 0 4 0 5 30 0.0 60 0 3.5 0 -1 0
 
 Podivná bomba
 Nepredvídateľná a vysoko deštruktívna
-30000 3 50 0.2 200 0 0 200 0 9 1 0 0 3 0 10 27 0.0 360 1.0 0.5 0.1 -1 0
 
 Podivná smrť
 Nepredvídateľná a extrémne deštruktívna
-50000 1 90 0.2 300 2 1 300 0 9 1 0 0 4 0 10 28 0.0 360 1.0 0.5 0.1 -1 0
 
 Podivná bombička
 Výbušná hlavica o strednej ráži
-7000 3 5 0.01 40 0 0 50 0 10 1 0 1 1 1 0 -1 0 0 0 0 0 125 1.0
 
 Podivná smrtiaca bombička
 Výbušná hlavica o veľkej ráži
-9000 2 9 0.01 100 2 1 90 0 10 1 0 1 3 1 0 -1 0 0 0 0 0 175 1.0
 
 Bombička
 Výbušná hlavica o strednej ráži
-3500 3 20 0.2 40 0 2 40 0 8 1 0 0 1 1 0 -1 0 0 0 0 0 -1 0
 
 Superbombička
 Výbušná hlavica o veľkej ráži
-5000 2 40 0.2 60 1 2 65 0 8 1 0 0 3 1 0 -1 0 0 0 0 0 -1 0
 
 Zavŕtavač
 Najprv sa zavŕta do povrchu, dobrá zbraň na zahrabané tanky. Zavŕtavač je určený pre použitie pod povrchom a nemá rád veľký odpor vzduchu.
-7500 3 100 0.4 50 0 2 50 0 25 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Prenikač
 Podpovrchová zbraň, ktoré po čase začne stúpať na povrch. Prenikač exploduje buď pri kontakte s povrchom alebo pri kontakte so zahrabanými objektami. Táto strela je určená pre hrabanie a preto nemá rada veľký odpor vzduchu.
-20000 2 150 0.4 80 0 2 100 0 26 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Malá napalmová bomba
 Rozhodí malé množstvo napalmu, ktorý pri kontakte intenzívne horí
-6000 3 80 0.2 80 1 1 80 0 9 1 0 0 3 0 10 36 0.0 90 0.2 2.5 1.0 -1 0
 
 Stredná napalmová bomba
 Rozhodí napalm, ktorý pri kontakte intenzívne horí
-14000 2 200 0.2 150 1 1 200 0 9 1 0 0 4 0 25 36 0.0 90 0.2 3 1.0 -1 0
 
 Veľká napalmová bomba
 Pokryje okolitú oblasť napalmom, ktorý pri kontakte intenzívne horí
-22000 1 400 0.2 200 1 2 500 0 9 1 0 0 5 0 60 36 0.0 90 0.2 5 1.0 -1 0
 
 Napalmové želé
 Intenzívne horiace chemické želé
-2000 5 5 0.5 10 1 10 30 0 0 1 0 0 2 1 0 -1 0 0 0 0 0 -1 0
 
 Vŕtač
 Vertikálna explózia, ktorý urobí malú škodu, ale vytvorí hlbokú dieru v zemi.
-5000 2 10 0.2 300 3 5 10 0 28 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Otras
 Pri kontakte spôsobí malé zemetrasenie
-3000 5 30 0.2 40 0 2 10 0 14 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Tlaková vlna
 Pri kontakte spôsobí veľké zemetrasenie
-10000 2 60 0.2 100 2 3 20 0 15 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Tektonický posun
 Pri kontakte spôsobí obrovské zemetrasenie
-30000 1 100 0.2 150 2 4 30 0 16 1 0 0 5 0 0 -1 0 0 0 0 0 -1 0
 
 Hlučná bomba
 Zničí malé množstvo zeme bez toho, aby poškodila čokoľvek iné
-2000 5 15 0.2 25 0 2 0 0 23 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Veľká hlučná bomba
 Zničí veľké množstvo zeminy bez toho, aby poškodila čokoľvek iné
-3000 2 30 0.2 100 1 2 0 0 24 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Hlučná nálož
 Priamo pred zbraňou vyreže do zeminy kužeľ
-1000 5 15 0.2 50 0 2 0 0 11 1 0 1 1 0 0 -1 0 0 0 0 0 0 0
 
 Hlučná detonácia
 Priamo pred zbraňou vyreže do zeminy veľký kužeľ
-2000 2 30 0.2 150 1 2 0 0 12 1 0 1 3 0 0 -1 0 0 0 0 0 0 0
 
 Guľa so zeminou
 Vytvorí malú guľku materiálu pre pochovanie vašich súperov
-3000 5 15 0.2 25 0 2 0 0 11 1 0 0 1 0 0 -1 0 0 0 0 0 -1 0
 
 Veľká guľa so zeminou
 Vytvorí veľkú guľu materiálu pre pochovanie vašich súperov
-7000 2 30 0.2 60 1 2 0 0 12 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Superguľa so zeminou
 Vytvorí obrovskú guľu materiálu pre pochovanie vašich súperov
-10000 1 60 0.2 100 2 2 0 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Slabý nános zeminy
 Zhodí malé kôpky zeminy na vašich nepriateľov
-4000 2 30 0.2 40 1 2 0 0 12 4 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Trieštivé MIRV
 Vypustí skupinku malých striel smerom dolu
-10000 2 100 0.2 150 0 2 200 0 7 1 0 0 2 0 5 29 0.0 45 0 2.0 0 -1 0
 
 Percentuálna bomba
 Zničí polovicu brnenia cieľa
-12000 2 30 0.2 30 2 4 0 0 29 1 0 0 3 0 0 -1 0 0 0 0 0 -1 0
 
 Redukovač
 Znižuje explozívnu silu nepriateľských striel.
-6000 3 25 0.3 30 2 4 0 0 30 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Malý laser
 50kW laserový lúč
-5000 5 1 0.0 2 0 0 30 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Stredný laser
 100kW laserový lúč
-10000 3 1 0.0 5 0 0 65 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 Veľký laser
 Silný 200kW laserový lúč
-15000 2 1 0.0 9 0 0 150 0 13 1 0 0 4 0 0 -1 0 0 0 0 0 -1 0
 
 *NATURALS*
 Malý meteorit
 Malé kusy skál z oblohy
-0 1 15 0.2 25 0 0 5 0 20 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Stredný meteorit
 Stredne veľké kusy skál z oblohy
-0 1 20 0.2 40 0 0 10 0 21 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Veľký meteorit
 Veľké kusy skál z oblohy
-0 1 25 0.2 60 1 1 20 0 22 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Slabý blesk
 Slabý blesk
-0 1 1 0.0 1 0 0 5 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Stredný blesk
 Stredne silný blesk
-0 1 1 0.0 4 0 0 15 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 Silný blesk
 Silný blesk
-0 1 1 0.0 7 0 0 35 0 0 1 0 0 0 0 0 -1 0 0 0 0 0 -1 0
 
 *ITEMS*
 Teleport
 Teleportuje tank na náhodné miesto
-5000 2 1 3 11
 
 Výmena
 Výmena miesta s iným tankom
-10000 2 1 4 11
 
 Hromadný teleport
 Teleportuje všetky tanky na obrazovke
-15000 2 1 4 11
 
 Ventilátor
 Zmení silu a smer vetra
-2500 3 1 3 2
 
 Pomsta
 Malé samo a autodeštrukčné zariadenie. Keď už musíte skončiť, tak prečo nevziať niekoho so sebou?
-20000 1 1 2 0 0 21
 
 Smrteľný hnev
 Veľké samo/autodeštrukčné zariadenie. Vzájomne zaistená deštrukcia.
-40000 1 1 3 0 21 3
 
 Osudové zúrenie
 Štýlové zakončenie
-60000 1 1 4 0 22 3
 
 Ľahký štít
 Malé množstvo ochrany pred poškodením
-10000 3 0 1 0 50 0 0 255 0 1
 
 Stredný štít
 Chráni pred poškodením
-20000 2 0 2 0 100 0 64 255 0 3
 
 Ťažký štít
 Veľké množstvo ochrany pred poškodením
-30000 2 0 4 0 150 0 128 255 64 6
 
 Ľahký odkloňovač striel
 Jemne odkloní nepriateľské strely
-10000 3 0 2 0 10 250 128 0 255 1
 
 Stredný odkloňovač striel
 Odkloní nepriateľské strely
-20000 2 0 3 0 20 500 192 64 255 3
 
 Ťažký odkloňovač striel
 Výrazne odkloní nepriateľské strely
-40000 1 0 5 0 40 1000 255 128 255 6
 
 Bežné opancierovanie
 Permanentne zvýši množstvo škody, ktorému váš tank dokáže odolať. Každý dodatočný nákup pridá o niečo menšie množstvo pancieru vášmu tanku.
-20000 1 0 2 0 300
 
 Plasteelové opancierovanie
 Permanentne zvýši množstvo škody, ktorému váš tank dokáže odolať. Každý dodatočný nákup pridá o niečo menšie množstvo pancieru vášmu tanku.
-40000 1 0 4 0 2155
 
 Zosilnenie intenzity
 Malé permanentné zvýšenie účinnosti vašich zbraní. Účinnosť má klesajúcu tendenciu a tak každý dodatočný nákup má stále slabší efekt.
-21000 1 0 3 0 0.10
 
 Mohutná sila
 Permanentné zvýšenie účinnosti vašich zbraní. Účinnosť má klesajúcu tendenciu a tak každý dodatočný nákup má stále slabší efekt.
-50000 1 0 3 0 0.30
 
 Klzké projektily
 Teflónová vrstva pre projektily, ktoré znížia odpor a účinok vetra
-1000 50 0 3 0 0.5
 
 Dierkované projektily
 Malé dierky v plášti projektilov pre veľké zníženie odporu
-2000 50 0 4 0 0.1
 
 Padák
 Umožní tanku pomaly klesať k zemi
-5000 10 0 0 0 0 0
 
 Súprava pre opravu
 Po každom ťahu opravuje tank. Každá ďalšia súprava poskytuje o niečo menšie brnenie.
-10000 1 0 2 0 0 0
 
 Palivo
 Umožňuje tanku pohybovať sa po vodorovnom teréne.
-1000 10 0 2 0 0 0
 
 Raketa
 Vypustí tank do vzduchu.
-2000 2 1 4 0 0 0
 
 SDI raketová obrana
 Poskytuje určitú ochranu proti prichádzajúcim strelám.
-10000 1 0 5 0 0
 
diff --git a/title/.directory b/title/.directory
deleted file mode 100644
index 43701d7..0000000
--- a/title/.directory
+++ /dev/null
@@ -1,3 +0,0 @@
-[Dolphin]
-ShowPreview=true
-Timestamp=2010,1,15,8,34,14
diff --git a/unicode.dat b/unicode.dat
index 331fc61..d0b6ea0 100644
Binary files a/unicode.dat and b/unicode.dat differ
diff --git a/vs12/README_allegro.txt b/vs12/README_allegro.txt
new file mode 100755
index 0000000..5180206
--- /dev/null
+++ b/vs12/README_allegro.txt
@@ -0,0 +1,14 @@
+To build atanks using Visual Studio 2013 you will need
+to adapt the include path settings to where your allegro
+includes are.
+Further replace alleg44.dll and alleg44.lib with the
+versions you want to use.
+
+Release 32bit uses alleg44.lib          and alleg44.dll
+Release 64bit uses alleg44_64.lib       and alleg44.dll
+Debug   32bit uses alleg44-debug.lib    and alleg44-debug.dll
+Debug   64bit uses alleg44_64-debug.lib and alleg44_64-debug.dll
+
+As the windows build is originally not meant for debugging,
+only the 32bit release versions are included in git.
+You will need your own includes and libs!
diff --git a/vs12/atanks.sln b/vs12/atanks.sln
new file mode 100755
index 0000000..dd03f5b
--- /dev/null
+++ b/vs12/atanks.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31206.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "atanks", "atanks.vcxproj", "{146B9823-15FD-45DC-8EA4-56670798D8D8}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Debug|Win32.ActiveCfg = Debug|Win32
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Debug|Win32.Build.0 = Debug|Win32
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Debug|x64.ActiveCfg = Debug|x64
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Debug|x64.Build.0 = Debug|x64
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Release|Win32.ActiveCfg = Release|Win32
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Release|Win32.Build.0 = Release|Win32
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Release|x64.ActiveCfg = Release|x64
+		{146B9823-15FD-45DC-8EA4-56670798D8D8}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/vs12/atanks.vcxproj b/vs12/atanks.vcxproj
new file mode 100755
index 0000000..0910dec
--- /dev/null
+++ b/vs12/atanks.vcxproj
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{146B9823-15FD-45DC-8EA4-56670798D8D8}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>atanks</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(SolutionDir)../</OutDir>
+    <IntDir>$(Configuration)_x86\</IntDir>
+    <TargetName>$(ProjectName)_d</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <TargetName>$(ProjectName)_64_d</TargetName>
+    <OutDir>$(SolutionDir)../</OutDir>
+    <IntDir>$(Configuration)_x64\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(SolutionDir)../</OutDir>
+    <IntDir>$(Configuration)_x86\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <TargetName>$(ProjectName)_64</TargetName>
+    <OutDir>$(SolutionDir)../</OutDir>
+    <IntDir>$(Configuration)_x64\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level2</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;VERSION="6.2-aiu1";ATANKS_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>false</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\allegro\include;$(SolutionDir)..\..\allegro\build_x86_d\include;$(SolutionDir)..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <AdditionalUsingDirectories>
+      </AdditionalUsingDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(SolutionDir)..</AdditionalLibraryDirectories>
+      <AdditionalDependencies>alleg44.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level2</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;VERSION="6.2-aiu1";ATANKS_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>false</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\allegro\include;$(SolutionDir)..\..\allegro\build_x86_d\include;$(SolutionDir)..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <AdditionalUsingDirectories>
+      </AdditionalUsingDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(SolutionDir)..</AdditionalLibraryDirectories>
+      <AdditionalDependencies>alleg44_64-debug.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level1</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;VERSION="6.2-aiu1";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>false</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\allegro\include;$(SolutionDir)..\..\allegro\build_x86\include;$(SolutionDir)..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <OmitFramePointers>true</OmitFramePointers>
+      <AdditionalUsingDirectories>
+      </AdditionalUsingDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>$(SolutionDir)..</AdditionalLibraryDirectories>
+      <AdditionalDependencies>alleg44.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level1</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;VERSION="6.2-aiu1";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>false</SDLCheck>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\..\allegro\include;$(SolutionDir)..\..\allegro\build_x86\include;$(SolutionDir)..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <OmitFramePointers>true</OmitFramePointers>
+      <AdditionalUsingDirectories>
+      </AdditionalUsingDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>$(SolutionDir)..</AdditionalLibraryDirectories>
+      <AdditionalDependencies>alleg44_64.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\src\aicore.h" />
+    <ClInclude Include="..\src\beam.h" />
+    <ClInclude Include="..\src\button.h" />
+    <ClInclude Include="..\src\client.h" />
+    <ClInclude Include="..\src\clock.h" />
+    <ClInclude Include="..\src\debris_pool.h" />
+    <ClInclude Include="..\src\debug.h" />
+    <ClInclude Include="..\src\decor.h" />
+    <ClInclude Include="..\src\environment.h" />
+    <ClInclude Include="..\src\explosion.h" />
+    <ClInclude Include="..\src\externs.h" />
+    <ClInclude Include="..\src\extern\dirent.h" />
+    <ClInclude Include="..\src\files.h" />
+    <ClInclude Include="..\src\floattext.h" />
+    <ClInclude Include="..\src\gameloop.h" />
+    <ClInclude Include="..\src\gfxData.h" />
+    <ClInclude Include="..\src\globaldata.h" />
+    <ClInclude Include="..\src\globals.h" />
+    <ClInclude Include="..\src\globaltypes.h" />
+    <ClInclude Include="..\src\imagedefs.h" />
+    <ClInclude Include="..\src\land.h" />
+    <ClInclude Include="..\src\main.h" />
+    <ClInclude Include="..\src\menu.h" />
+    <ClInclude Include="..\src\missile.h" />
+    <ClInclude Include="..\src\network.h" />
+    <ClInclude Include="..\src\optioncontent.h" />
+    <ClInclude Include="..\src\optionitem.h" />
+    <ClInclude Include="..\src\optionitembase.h" />
+    <ClInclude Include="..\src\optionitemcolour.h" />
+    <ClInclude Include="..\src\optionitemmenu.h" />
+    <ClInclude Include="..\src\optionitemplayer.h" />
+    <ClInclude Include="..\src\optionscreens.h" />
+    <ClInclude Include="..\src\optiontypes.h" />
+    <ClInclude Include="..\src\physobj.h" />
+    <ClInclude Include="..\src\player.h" />
+    <ClInclude Include="..\src\player_types.h" />
+    <ClInclude Include="..\src\resource.h" />
+    <ClInclude Include="..\src\satellite.h" />
+    <ClInclude Include="..\src\shop.h" />
+    <ClInclude Include="..\src\sky.h" />
+    <ClInclude Include="..\src\sound.h" />
+    <ClInclude Include="..\src\tank.h" />
+    <ClInclude Include="..\src\teleport.h" />
+    <ClInclude Include="..\src\text.h" />
+    <ClInclude Include="..\src\update.h" />
+    <ClInclude Include="..\src\virtobj.h" />
+    <ClInclude Include="..\src\winclock.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\src\aicore.cpp" />
+    <ClCompile Include="..\src\atanks.cpp" />
+    <ClCompile Include="..\src\beam.cpp" />
+    <ClCompile Include="..\src\button.cpp" />
+    <ClCompile Include="..\src\client.cpp" />
+    <ClCompile Include="..\src\clock.cpp" />
+    <ClCompile Include="..\src\debris_pool.cpp" />
+    <ClCompile Include="..\src\debug.cpp" />
+    <ClCompile Include="..\src\decor.cpp" />
+    <ClCompile Include="..\src\environment.cpp" />
+    <ClCompile Include="..\src\explosion.cpp" />
+    <ClCompile Include="..\src\extern\dirent.c" />
+    <ClCompile Include="..\src\files.cpp" />
+    <ClCompile Include="..\src\floattext.cpp" />
+    <ClCompile Include="..\src\gameloop.cpp" />
+    <ClCompile Include="..\src\gfxData.cpp" />
+    <ClCompile Include="..\src\globaldata.cpp" />
+    <ClCompile Include="..\src\globaltypes.cpp" />
+    <ClCompile Include="..\src\land.cpp" />
+    <ClCompile Include="..\src\main.cpp" />
+    <ClCompile Include="..\src\menu.cpp" />
+    <ClCompile Include="..\src\missile.cpp" />
+    <ClCompile Include="..\src\network.cpp" />
+    <ClCompile Include="..\src\optionitembase.cpp" />
+    <ClCompile Include="..\src\optionitemcolour.cpp" />
+    <ClCompile Include="..\src\optionitemmenu.cpp" />
+    <ClCompile Include="..\src\optionitemplayer.cpp" />
+    <ClCompile Include="..\src\optionscreens.cpp" />
+    <ClCompile Include="..\src\optiontypes.cpp" />
+    <ClCompile Include="..\src\perlin.cpp" />
+    <ClCompile Include="..\src\physobj.cpp" />
+    <ClCompile Include="..\src\player.cpp" />
+    <ClCompile Include="..\src\player_types.cpp" />
+    <ClCompile Include="..\src\satellite.cpp" />
+    <ClCompile Include="..\src\shop.cpp" />
+    <ClCompile Include="..\src\sky.cpp" />
+    <ClCompile Include="..\src\sound.cpp" />
+    <ClCompile Include="..\src\tank.cpp" />
+    <ClCompile Include="..\src\teleport.cpp" />
+    <ClCompile Include="..\src\text.cpp" />
+    <ClCompile Include="..\src\update.cpp" />
+    <ClCompile Include="..\src\virtobj.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\src\atanks.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/vs12/atanks.vcxproj.filters b/vs12/atanks.vcxproj.filters
new file mode 100755
index 0000000..597db3f
--- /dev/null
+++ b/vs12/atanks.vcxproj.filters
@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Quelldateien">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Headerdateien">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Ressourcendateien">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\src\aicore.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\beam.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\button.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\client.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\clock.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\debris_pool.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\debug.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\decor.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\environment.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\explosion.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\externs.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\files.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\floattext.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\gameloop.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\gfxData.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\globaldata.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\globals.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\globaltypes.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\imagedefs.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\land.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\main.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\menu.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\missile.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\network.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optioncontent.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optionitem.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optionitembase.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optionitemmenu.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optionitemplayer.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optionscreens.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optiontypes.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\physobj.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\player.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\player_types.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\resource.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\satellite.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\shop.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\sky.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\sound.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\tank.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\teleport.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\text.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\update.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\virtobj.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\winclock.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\extern\dirent.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\optionitemcolour.h">
+      <Filter>Headerdateien</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\src\aicore.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\atanks.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\beam.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\button.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\client.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\clock.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\debris_pool.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\debug.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\decor.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\environment.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\explosion.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\files.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\floattext.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\gameloop.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\gfxData.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\globaldata.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\globaltypes.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\land.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\main.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\menu.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\missile.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\network.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\optionitembase.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\optionitemmenu.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\optionitemplayer.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\optionscreens.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\optiontypes.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\perlin.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\physobj.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\player.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\player_types.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\satellite.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\shop.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\sky.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\sound.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\tank.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\teleport.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\text.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\update.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\virtobj.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\extern\dirent.c">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\optionitemcolour.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\src\atanks.rc">
+      <Filter>Ressourcendateien</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/atanks.git



More information about the Pkg-games-commits mailing list