Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.  * OpenTyrian: A modern cross-platform port of Tyrian
  3.  * Copyright (C) 2007-2009  The OpenTyrian Development Team
  4.  *
  5.  * This program is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU General Public License
  7.  * as published by the Free Software Foundation; either version 2
  8.  * of the License, or (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18.  */
  19.  
  20. /* File notes:
  21.  * Two players duke it out in a Scorched Earth style game.
  22.  * Most of the variables referring to the players are global as
  23.  * they are often edited and that's how the original was written.
  24.  *
  25.  * Currently this file is at its final stage for vanilla destruct.
  26.  * Almost all of the left/right code duplications is gone.  Most of the
  27.  * functions have been examined and tightened up, none of the enums
  28.  * start with '1', and the various large functions have been divided into
  29.  * smaller chunks.
  30.  *
  31.  * Destruct also supports some 'hidden' configuration that's just too awesome
  32.  * to not have available.  Destruct has no configuration options in game, but
  33.  * that doesn't stop us from changing various limiting vars and letting
  34.  * people remap the keyboard.  AIs may also be introduced here; fighting a
  35.  * stateless AI isn't really challenging afterall.
  36.  *
  37.  * This hidden config also allows for a hidden game mode!  Though as a custom
  38.  * game mode wouldn't show up in the data files it forces us to distinguish
  39.  * between the constant DESTRUCT_MODES (5) and MAX_MODES (6).  DESTRUCT_MODES
  40.  * is only used with loaded data.
  41.  *
  42.  * Things I wanted to do but can't: Remove references to VGAScreen.  For
  43.  * a multitude of reasons this just isn't feasable.  It would have been nice
  44.  * to increase the playing field though...
  45.  */
  46.  
  47. /*** Headers ***/
  48. #include "destruct.h"
  49.  
  50. #include "config.h"
  51. #include "config_file.h"
  52. #include "fonthand.h"
  53. #include "helptext.h"
  54. #include "keyboard.h"
  55. #include "loudness.h"
  56. #include "mtrand.h"
  57. #include "nortsong.h"
  58. #include "opentyr.h"
  59. #include "palette.h"
  60. #include "picload.h"
  61. #include "sprite.h"
  62. #include "varz.h"
  63. #include "vga256d.h"
  64. #include "video.h"
  65.  
  66. #include <assert.h>
  67.  
  68. /*** Defines ***/
  69. #define UNIT_HEIGHT 12
  70. #define MAX_KEY_OPTIONS 4
  71.  
  72. /*** Enums ***/
  73. enum de_state_t { STATE_INIT, STATE_RELOAD, STATE_CONTINUE };
  74. enum de_player_t { PLAYER_LEFT = 0, PLAYER_RIGHT = 1, MAX_PLAYERS = 2 };
  75. enum de_team_t { TEAM_LEFT = 0, TEAM_RIGHT = 1, MAX_TEAMS = 2 };
  76. enum de_mode_t { MODE_5CARDWAR = 0, MODE_TRADITIONAL, MODE_HELIASSAULT,
  77.                  MODE_HELIDEFENSE, MODE_OUTGUNNED, MODE_CUSTOM,
  78.                  MODE_FIRST = MODE_5CARDWAR, MODE_LAST = MODE_CUSTOM,
  79.                  MAX_MODES = 6, MODE_NONE = -1 };
  80. enum de_unit_t { UNIT_TANK = 0, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE,
  81.                  UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI,
  82.                  UNIT_FIRST = UNIT_TANK, UNIT_LAST = UNIT_HELI,
  83.                  MAX_UNITS = 8, UNIT_NONE = -1 };
  84. enum de_shot_t { SHOT_TRACER = 0, SHOT_SMALL, SHOT_LARGE, SHOT_MICRO,
  85.                  SHOT_SUPER, SHOT_DEMO, SHOT_SMALLNUKE, SHOT_LARGENUKE,
  86.                  SHOT_SMALLDIRT, SHOT_LARGEDIRT, SHOT_MAGNET, SHOT_MINILASER,
  87.                  SHOT_MEGALASER, SHOT_LASERTRACER, SHOT_MEGABLAST, SHOT_MINI,
  88.                  SHOT_BOMB,
  89.                  SHOT_FIRST = SHOT_TRACER, SHOT_LAST = SHOT_BOMB,
  90.                  MAX_SHOT_TYPES = 17, SHOT_INVALID = -1 };
  91. enum de_expl_t { EXPL_NONE, EXPL_MAGNET, EXPL_DIRT, EXPL_NORMAL }; /* this needs a better name */
  92. enum de_trails_t { TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL };
  93. enum de_pixel_t { PIXEL_BLACK = 0, PIXEL_DIRT = 25 };
  94. enum de_mapflags_t { MAP_NORMAL = 0x00, MAP_WALLS = 0x01, MAP_RINGS = 0x02,
  95.                                          MAP_HOLES = 0x04, MAP_FUZZY = 0x08, MAP_TALL = 0x10 };
  96.  
  97. /* keys and moves should line up. */
  98. enum de_keys_t {  KEY_LEFT = 0,  KEY_RIGHT,  KEY_UP,  KEY_DOWN,  KEY_CHANGE,  KEY_FIRE,  KEY_CYUP,  KEY_CYDN,  MAX_KEY = 8};
  99. enum de_move_t { MOVE_LEFT = 0, MOVE_RIGHT, MOVE_UP, MOVE_DOWN, MOVE_CHANGE, MOVE_FIRE, MOVE_CYUP, MOVE_CYDN, MAX_MOVE = 8};
  100.  
  101. /* The tracerlaser is dummied out.  It works but (probably due to the low
  102.  * MAX_SHOTS) is not assigned to anything.  The bomb does not work.
  103.  */
  104.  
  105.  
  106. /*** Structs ***/
  107. struct destruct_config_s {
  108.  
  109.         unsigned int max_shots;
  110.         unsigned int min_walls;
  111.         unsigned int max_walls;
  112.         unsigned int max_explosions;
  113.         unsigned int max_installations;
  114.         bool allow_custom;
  115.         bool alwaysalias;
  116.         bool jumper_straight[2];
  117.         bool ai[2];
  118. };
  119. struct destruct_unit_s {
  120.  
  121.         /* Positioning/movement */
  122.         unsigned int unitX; /* yep, one's an int and the other is a real */
  123.         float        unitY;
  124.         float        unitYMov;
  125.         bool         isYInAir;
  126.  
  127.         /* What it is and what it fires */
  128.         enum de_unit_t unitType;
  129.         enum de_shot_t shotType;
  130.  
  131.         /* What it's pointed */
  132.         float angle;
  133.         float power;
  134.  
  135.         /* Misc */
  136.         int lastMove;
  137.         unsigned int ani_frame;
  138.         int health;
  139. };
  140. struct destruct_shot_s {
  141.  
  142.         bool isAvailable;
  143.  
  144.         float x;
  145.         float y;
  146.         float xmov;
  147.         float ymov;
  148.         bool gravity;
  149.         unsigned int shottype;
  150.         //int shotdur; /* This looks to be unused */
  151.         unsigned int trailx[4], traily[4], trailc[4];
  152. };
  153. struct destruct_explo_s {
  154.  
  155.         bool isAvailable;
  156.  
  157.         unsigned int x, y;
  158.         unsigned int explowidth;
  159.         unsigned int explomax;
  160.         unsigned int explofill;
  161.         enum de_expl_t exploType;
  162. };
  163. struct destruct_moves_s {
  164.         bool actions[MAX_MOVE];
  165. };
  166. struct destruct_keys_s {
  167.         SDLKey Config[MAX_KEY][MAX_KEY_OPTIONS];
  168. };
  169. struct destruct_ai_s {
  170.  
  171.         int c_Angle, c_Power, c_Fire;
  172.         unsigned int c_noDown;
  173. };
  174. struct destruct_player_s {
  175.  
  176.         bool is_cpu;
  177.         struct destruct_ai_s aiMemory;
  178.  
  179.         struct destruct_unit_s * unit;
  180.         struct destruct_moves_s moves;
  181.         struct destruct_keys_s  keys;
  182.  
  183.         enum de_team_t team;
  184.         unsigned int unitsRemaining;
  185.         unsigned int unitSelected;
  186.         unsigned int shotDelay;
  187.         unsigned int score;
  188. };
  189. struct destruct_wall_s {
  190.  
  191.         bool wallExist;
  192.         unsigned int wallX, wallY;
  193. };
  194. struct destruct_world_s {
  195.  
  196.         /* Map data & screen pointer */
  197.         unsigned int baseMap[320];
  198.         SDL_Surface * VGAScreen;
  199.         struct destruct_wall_s * mapWalls;
  200.  
  201.         /* Map configuration */
  202.         enum de_mode_t destructMode;
  203.         unsigned int mapFlags;
  204. };
  205.  
  206. /*** Function decs ***/
  207. //Prep functions
  208. static void JE_destructMain( void );
  209. static void JE_introScreen( void );
  210. static enum de_mode_t JE_modeSelect( void );
  211. static void JE_helpScreen( void );
  212. static void JE_pauseScreen( void );
  213.  
  214. //level generating functions
  215. static void JE_generateTerrain( void );
  216. static void DE_generateBaseTerrain( unsigned int, unsigned int *);
  217. static void DE_drawBaseTerrain( unsigned int * );
  218. static void DE_generateUnits( unsigned int * );
  219. static void DE_generateWalls( struct destruct_world_s * );
  220. static void DE_generateRings(SDL_Surface *, Uint8 );
  221. static void DE_ResetLevel( void );
  222. static unsigned int JE_placementPosition( unsigned int, unsigned int, unsigned int * );
  223.  
  224. //drawing functions
  225. static void JE_aliasDirt( SDL_Surface * );
  226. static void DE_RunTickDrawCrosshairs( void );
  227. static void DE_RunTickDrawHUD( void );
  228. static void DE_GravityDrawUnit( enum de_player_t, struct destruct_unit_s * );
  229. static void DE_RunTickAnimate( void );
  230. static void DE_RunTickDrawWalls( void );
  231. static void DE_DrawTrails( struct destruct_shot_s *, unsigned int, unsigned int, unsigned int );
  232. static void JE_tempScreenChecking( void );
  233. static void JE_superPixel( unsigned int, unsigned int );
  234. static void JE_pixCool( unsigned int, unsigned int, Uint8 );
  235.  
  236. //player functions
  237. static void DE_RunTickGetInput( void );
  238. static void DE_ProcessInput( void );
  239. static void DE_ResetPlayers( void );
  240. static void DE_ResetAI( void );
  241. static void DE_ResetActions( void );
  242. static void DE_RunTickAI( void );
  243.  
  244. //unit functions
  245. static void DE_RaiseAngle( struct destruct_unit_s * );
  246. static void DE_LowerAngle( struct destruct_unit_s * );
  247. static void DE_RaisePower( struct destruct_unit_s * );
  248. static void DE_LowerPower( struct destruct_unit_s * );
  249. static void DE_CycleWeaponUp( struct destruct_unit_s * );
  250. static void DE_CycleWeaponDown( struct destruct_unit_s * );
  251. static void DE_RunMagnet( enum de_player_t, struct destruct_unit_s * );
  252. static void DE_GravityFlyUnit( struct destruct_unit_s * );
  253. static void DE_GravityLowerUnit( struct destruct_unit_s * );
  254. static void DE_DestroyUnit( enum de_player_t, struct destruct_unit_s * );
  255. static void DE_ResetUnits( void );
  256. static inline bool DE_isValidUnit( struct destruct_unit_s *);
  257.  
  258. //weapon functions
  259. static void DE_ResetWeapons( void );
  260. static void DE_RunTickShots( void );
  261. static void DE_RunTickExplosions( void );
  262. static void DE_TestExplosionCollision( unsigned int, unsigned int);
  263. static void JE_makeExplosion( unsigned int, unsigned int, enum de_shot_t );
  264. static void DE_MakeShot( enum de_player_t, const struct destruct_unit_s *, int );
  265.  
  266. //gameplay functions
  267. static enum de_state_t DE_RunTick( void );
  268. static void DE_RunTickCycleDeadUnits( void );
  269. static void DE_RunTickGravity( void );
  270. static bool DE_RunTickCheckEndgame( void );
  271. static bool JE_stabilityCheck( unsigned int, unsigned int );
  272.  
  273. //sound
  274. static void DE_RunTickPlaySounds( void );
  275. static void JE_eSound( unsigned int );
  276.  
  277.  
  278.  
  279. /*** Weapon configurations ***/
  280.  
  281. /* Part of me wants to leave these as bytes to save space. */
  282. static const bool     demolish[MAX_SHOT_TYPES] = {false, false, false, false, false, true, true, true, false, false, false, false, true, false, true, false, true};
  283. //static const int        shotGr[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101};
  284. static const int     shotTrail[MAX_SHOT_TYPES] = {TRAILS_NONE, TRAILS_NONE, TRAILS_NONE, TRAILS_NORMAL, TRAILS_NORMAL, TRAILS_NORMAL, TRAILS_FULL, TRAILS_FULL, TRAILS_NONE, TRAILS_NONE, TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL, TRAILS_NORMAL, TRAILS_FULL, TRAILS_NORMAL, TRAILS_NONE};
  285. //static const int      shotFuse[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0};
  286. static const int     shotDelay[MAX_SHOT_TYPES] = {10, 30, 80, 20, 60, 100, 140, 200, 20, 60, 5, 15, 50, 5, 80, 16, 0};
  287. static const int     shotSound[MAX_SHOT_TYPES] = {S_SELECT, S_WEAPON_2, S_WEAPON_1, S_WEAPON_7, S_WEAPON_7, S_EXPLOSION_9, S_EXPLOSION_22, S_EXPLOSION_22, S_WEAPON_5, S_WEAPON_13, S_WEAPON_10, S_WEAPON_15, S_WEAPON_15, S_WEAPON_26, S_WEAPON_14, S_WEAPON_7, S_WEAPON_7};
  288. static const int     exploSize[MAX_SHOT_TYPES] = {4, 20, 30, 14, 22, 16, 40, 60, 10, 30, 0, 5, 10, 3, 15, 7, 0};
  289. static const bool   shotBounce[MAX_SHOT_TYPES] = {false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true};
  290. static const int  exploDensity[MAX_SHOT_TYPES] = {  2,  5, 10, 15, 20, 15, 25, 30, 40, 80, 0, 30, 30,  4, 30, 5, 0};
  291. static const int      shotDirt[MAX_SHOT_TYPES] = {EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_DIRT, EXPL_DIRT, EXPL_MAGNET, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NONE};
  292. static const int     shotColor[MAX_SHOT_TYPES] = {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 16, 0};
  293.  
  294. static const int     defaultWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO,     SHOT_SMALLDIRT, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI};
  295. static const int  defaultCpuWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO,     SHOT_DEMO,      SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI};
  296. static const int defaultCpuWeaponB[MAX_UNITS] = {SHOT_DEMO,  SHOT_SMALLNUKE, SHOT_DEMO,      SHOT_INVALID, SHOT_MAGNET, SHOT_MEGALASER, SHOT_MICRO, SHOT_MINI};
  297. static const int       systemAngle[MAX_UNITS] = {true, true, true, false, false, true, false, false};
  298. static const int        baseDamage[MAX_UNITS] = {200, 120, 400, 300, 80, 150, 600, 40};
  299. static const int         systemAni[MAX_UNITS] = {false, false, false, true, false, false, false, true};
  300.  
  301. static bool weaponSystems[MAX_UNITS][MAX_SHOT_TYPES] =
  302. {
  303.         {1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // normal
  304.         {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // nuke
  305.         {0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, // dirt
  306.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // worthless
  307.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // magnet
  308.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, // laser
  309.         {1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, // jumper
  310.         {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}  // helicopter
  311. };
  312.  
  313. /* More constant configuration settings. */
  314. /* Music that destruct will play.  You can check out musmast.c to see what is what. */
  315. static const JE_byte goodsel[14] /*[1..14]*/ = {1, 2, 6, 12, 13, 14, 17, 23, 24, 26, 28, 29, 32, 33};
  316.  
  317. /* Unit creation.  Need to move this later: Doesn't belong here */
  318. static JE_byte basetypes[10][11] /*[1..8, 1..11]*/ = /* [0] is amount of units*/
  319. {
  320.         {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT,      UNIT_DIRT,   UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER,  UNIT_JUMPER, UNIT_HELI},   /*Normal*/
  321.         {1, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK,      UNIT_TANK,   UNIT_TANK,      UNIT_TANK,   UNIT_TANK,   UNIT_TANK,   UNIT_TANK},   /*Traditional*/
  322.         {4, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI,      UNIT_HELI,   UNIT_HELI,      UNIT_HELI,   UNIT_HELI,   UNIT_HELI,   UNIT_HELI},   /*Weak   Heli attack fleet*/
  323.         {8, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_NUKE,      UNIT_NUKE,   UNIT_NUKE,      UNIT_DIRT,   UNIT_MAGNET, UNIT_LASER,  UNIT_JUMPER}, /*Strong Heli defense fleet*/
  324.         {8, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI,      UNIT_HELI,   UNIT_HELI,      UNIT_HELI,   UNIT_HELI,   UNIT_HELI,   UNIT_HELI},   /*Strong Heli attack fleet*/
  325.         {4, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK,      UNIT_NUKE,   UNIT_NUKE,      UNIT_DIRT,   UNIT_MAGNET, UNIT_JUMPER, UNIT_JUMPER}, /*Weak   Heli defense fleet*/
  326.         {8, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER,     UNIT_JUMPER, UNIT_HELI,   UNIT_TANK,   UNIT_NUKE},   /*Overpowering fleet*/
  327.         {4, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT,      UNIT_TANK,   UNIT_LASER,     UNIT_JUMPER, UNIT_HELI,   UNIT_NUKE,   UNIT_JUMPER}, /*Weak fleet*/
  328.         {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT,      UNIT_DIRT,   UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER,  UNIT_JUMPER, UNIT_HELI},   /*Left custom*/
  329.         {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT,      UNIT_DIRT,   UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER,  UNIT_JUMPER, UNIT_HELI},   /*Right custom*/
  330. };
  331. static const unsigned int baseLookup[MAX_PLAYERS][MAX_MODES] =
  332. {
  333.         {0, 1, 3, 4, 6, 8},
  334.         {0, 1, 2, 5, 7, 9}
  335. };
  336.  
  337.  
  338. static const JE_byte GraphicBase[MAX_PLAYERS][MAX_UNITS] =
  339. {
  340.         {  1,   6,  11,  58,  63,  68,  96, 153},
  341.         { 20,  25,  30,  77,  82,  87, 115, 172}
  342. };
  343.  
  344. static const JE_byte ModeScore[MAX_PLAYERS][MAX_MODES] =
  345. {
  346.         {1, 0, 0, 5, 0, 1},
  347.         {1, 0, 5, 0, 1, 1}
  348. };
  349.  
  350. static SDLKey defaultKeyConfig[MAX_PLAYERS][MAX_KEY][MAX_KEY_OPTIONS] =
  351. {
  352.         {       {SDLK_c},
  353.                 {SDLK_v},
  354.                 {SDLK_a},
  355.                 {SDLK_z},
  356.                 {SDLK_LALT},
  357.                 {SDLK_x, SDLK_LSHIFT},
  358.                 {SDLK_LCTRL},
  359.                 {SDLK_SPACE}
  360.         },
  361.         {       {SDLK_LEFT, SDLK_KP4},
  362.                 {SDLK_RIGHT, SDLK_KP6},
  363.                 {SDLK_UP, SDLK_KP8},
  364.                 {SDLK_DOWN, SDLK_KP2},
  365.                 {SDLK_BACKSLASH, SDLK_KP5},
  366.                 {SDLK_INSERT, SDLK_RETURN, SDLK_KP0, SDLK_KP_ENTER},
  367.                 {SDLK_PAGEUP, SDLK_KP9},
  368.                 {SDLK_PAGEDOWN, SDLK_KP3}
  369.         }
  370. };
  371.  
  372.  
  373. /*** Globals ***/
  374. static SDL_Surface *destructTempScreen;
  375. static JE_boolean destructFirstTime;
  376.  
  377. static struct destruct_config_s config = { 40, 20, 20, 40, 10, false, false, {true, false}, {true, false} };
  378. static struct destruct_player_s destruct_player[MAX_PLAYERS];
  379. static struct destruct_world_s  world;
  380. static struct destruct_shot_s   * shotRec;
  381. static struct destruct_explo_s  * exploRec;
  382.  
  383.  
  384. static const char *player_names[] =
  385. {
  386.         "left", "right",
  387. };
  388.  
  389. static const char *key_names[] =
  390. {
  391.         "left", "right", "up", "down",
  392.         "change", "fire", "previous weapon", "next weapon",
  393. };
  394.  
  395. static const char *unit_names[] =
  396. {
  397.         "tank", "nuke", "dirt", "satellite",
  398.         "magnet", "laser", "jumper", "heli",
  399. };
  400.  
  401. static enum de_unit_t get_unit_by_name( const char *unit_name )
  402. {
  403.         for (enum de_unit_t unit = UNIT_FIRST; unit < MAX_UNITS; ++unit)
  404.                 if (strcmp(unit_name, unit_names[unit]) == 0)
  405.                         return unit;
  406.        
  407.         return UNIT_NONE;
  408. }
  409.  
  410. static SDLKey get_SDLKey_by_name( const char *key_name )
  411. {
  412.         for (SDLKey key = SDLK_FIRST; key < SDLK_LAST; ++key)
  413.                 if (strcmp(key_name, SDL_GetKeyName(key)) == 0)
  414.                         return key;
  415.        
  416.         return SDLK_UNKNOWN;
  417. }
  418.  
  419. static void load_destruct_config( Config *config_ )
  420. {
  421.         ConfigSection *section;
  422.        
  423.         section = config_find_or_add_section(config_, "destruct", NULL);
  424.         if (section == NULL)
  425.                 exit(EXIT_FAILURE);  // out of memory
  426.        
  427.         config.alwaysalias = config_get_or_set_bool_option(section, "antialias craters", false, NO_YES);
  428.        
  429.         weaponSystems[UNIT_LASER][SHOT_LASERTRACER] = config_get_or_set_bool_option(section, "tracer laser", false, OFF_ON);
  430.        
  431.         config.max_shots = config_get_or_set_int_option(section, "max shots", 40);
  432.         config.max_explosions = config_get_or_set_int_option(section, "max explosions", 40);
  433.         config.min_walls = config_get_or_set_int_option(section, "min walls", 20);
  434.         config.max_walls = config_get_or_set_int_option(section, "max walls", 20);
  435.        
  436.         config.ai[0] = config_get_or_set_bool_option(section, "left ai", true, NO_YES);
  437.         config.jumper_straight[0] = config_get_or_set_bool_option(section, "left jumper fires straight", true, NO_YES);
  438.         config.ai[1] = config_get_or_set_bool_option(section, "right ai", false, NO_YES);
  439.         config.jumper_straight[1] = config_get_or_set_bool_option(section, "right jumper fires straight", false, NO_YES);
  440.        
  441.         // keyboard controls
  442.        
  443.         for (int p = 0; p < MAX_PLAYERS; ++p)
  444.         {
  445.                 section = config_find_section(config_, "destruct keyboard", player_names[p]);
  446.                 if (section == NULL)
  447.                         if ((section = config_add_section(config_, "destruct keyboard", player_names[p])) == NULL)
  448.                                 exit(-1);
  449.                
  450.                 ConfigOption *option;
  451.                
  452.                 for (int k = 0; k < MAX_KEY; ++k)
  453.                 {
  454.                         if ((option = config_get_or_set_option(section, key_names[k], NULL)) == NULL)
  455.                                 exit(-1);
  456.                        
  457.                         foreach_option_i_value(i, value, option)
  458.                         {
  459.                                 SDLKey key = get_SDLKey_by_name(value);
  460.                                 if (key != SDLK_LAST && i < COUNTOF(defaultKeyConfig[p][k]))
  461.                                 {
  462.                                         defaultKeyConfig[p][k][i] = key;
  463.                                 }
  464.                                 else  // invalid or excess
  465.                                 {
  466.                                         foreach_remove_option_value();
  467.                                         continue;
  468.                                 }
  469.                         }
  470.                        
  471.                         if (config_get_value_count(option) > 0)
  472.                         {
  473.                                 // unset remaining defaults
  474.                                 for (unsigned int i = config_get_value_count(option); i < COUNTOF(defaultKeyConfig[p][k]); ++i)
  475.                                         defaultKeyConfig[p][k][i] = SDLK_UNKNOWN;
  476.                         }
  477.                         else
  478.                         {
  479.                                 // set defaults
  480.                                 for (unsigned int i = 0; i < COUNTOF(defaultKeyConfig[p][k]); ++i)
  481.                                         if (defaultKeyConfig[p][k][i] != SDLK_UNKNOWN)
  482.                                                 config_add_value(option, SDL_GetKeyName(defaultKeyConfig[p][k][i]));
  483.                         }
  484.                 }
  485.         }
  486.        
  487.         // custom destruct mode
  488.        
  489.         section = config_find_section(config_, "destruct custom", NULL);
  490.         if (section == NULL)
  491.                 if ((section = config_add_section(config_, "destruct custom", NULL)) == NULL)
  492.                         exit(-1);
  493.        
  494.         config.allow_custom = config_get_or_set_bool_option(section, "enable", false, NO_YES);
  495.        
  496.         char buffer[15 + 1];
  497.        
  498.         for (int p = 0; p < MAX_PLAYERS; ++p)
  499.         {
  500.                 snprintf(buffer, sizeof(buffer), "%s num units", player_names[p]);
  501.                 basetypes[8 + p][0] = config_get_or_set_int_option(section, buffer, basetypes[8 + p][0]);
  502.                
  503.                 ConfigOption *option;
  504.                
  505.                 snprintf(buffer, sizeof(buffer), "%s unit", player_names[p]);
  506.                 if ((option = config_get_or_set_option(section, buffer, NULL)) == NULL)
  507.                         exit(-1);
  508.                
  509.                 foreach_option_i_value(i, value, option)
  510.                 {
  511.                         enum de_unit_t unit = get_unit_by_name(value);
  512.                         if (unit != UNIT_NONE && 1 + i < COUNTOF(basetypes[8 + p]))
  513.                         {
  514.                                 basetypes[8 + p][1 + i] = unit;
  515.                         }
  516.                         else  // invalid or excess
  517.                         {
  518.                                 foreach_remove_option_value();
  519.                                 continue;
  520.                         }
  521.                 }
  522.                
  523.                 if (config_get_value_count(option) > 0)
  524.                 {
  525.                         // set remaining units to tank
  526.                         for (unsigned int i = config_get_value_count(option); 1 + i < COUNTOF(basetypes[8 + p]); ++i)
  527.                         {
  528.                                 basetypes[8 + p][1 + i] = UNIT_TANK;
  529.                                 config_add_value(option, unit_names[UNIT_TANK]);
  530.                         }
  531.                 }
  532.                 else
  533.                 {
  534.                         // set defaults
  535.                         for (unsigned int i = 0; 1 + i < COUNTOF(basetypes[8 + p]); ++i)
  536.                                 config_add_value(option, unit_names[basetypes[8 + p][1 + i]]);
  537.                 }
  538.         }
  539. }
  540.  
  541. /*** Startup ***/
  542.  
  543. void JE_destructGame( void )
  544. {
  545.         unsigned int i;
  546.  
  547.         /* This is the entry function.  Any one-time actions we need to
  548.          * perform can go in here. */
  549.         JE_clr256(VGAScreen);
  550.         JE_showVGA();
  551.  
  552.         load_destruct_config(&opentyrian_config);
  553.  
  554.         //malloc things that have customizable sizes
  555.         shotRec  = malloc(sizeof(struct destruct_shot_s)  * config.max_shots);
  556.         exploRec = malloc(sizeof(struct destruct_explo_s) * config.max_explosions);
  557.         world.mapWalls = malloc(sizeof(struct destruct_wall_s) * config.max_walls);
  558.  
  559.         //Malloc enough structures to cover all of this session's possible needs.
  560.         for(i = 0; i < 10; i++) {
  561.                 config.max_installations = MAX(config.max_installations, basetypes[i][0]);
  562.         }
  563.         destruct_player[PLAYER_LEFT ].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations);
  564.         destruct_player[PLAYER_RIGHT].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations);
  565.  
  566.         destructTempScreen = game_screen;
  567.         world.VGAScreen = VGAScreen;
  568.  
  569.         JE_loadCompShapes(&eShapes[0], '~');
  570.         fade_black(1);
  571.  
  572.         JE_destructMain();
  573.  
  574.         //and of course exit actions go here.
  575.         free(shotRec);
  576.         free(exploRec);
  577.         free(world.mapWalls);
  578.         free(destruct_player[PLAYER_LEFT ].unit);
  579.         free(destruct_player[PLAYER_RIGHT].unit);
  580. }
  581.  
  582. static void JE_destructMain( void )
  583. {
  584.         enum de_state_t curState;
  585.  
  586.  
  587.         JE_loadPic(VGAScreen, 11, false);
  588.         JE_introScreen();
  589.  
  590.         DE_ResetPlayers();
  591.  
  592.         destruct_player[PLAYER_LEFT ].is_cpu = config.ai[PLAYER_LEFT];
  593.         destruct_player[PLAYER_RIGHT].is_cpu = config.ai[PLAYER_RIGHT];
  594.  
  595.         while(1)
  596.         {
  597.                 world.destructMode = JE_modeSelect();
  598.  
  599.                 if(world.destructMode == MODE_NONE) {
  600.                         break; /* User is quitting */
  601.                 }
  602.  
  603.                 do
  604.                 {
  605.  
  606.                         destructFirstTime = true;
  607.                         JE_loadPic(VGAScreen, 11, false);
  608.  
  609.                         DE_ResetUnits();
  610.                         DE_ResetLevel();
  611.                         do {
  612.                                 curState = DE_RunTick();
  613.                         } while(curState == STATE_CONTINUE);
  614.  
  615.                         fade_black(25);
  616.                 }
  617.                 while (curState == STATE_RELOAD);
  618.         }
  619. }
  620.  
  621. static void JE_introScreen( void )
  622. {
  623.         memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
  624.         JE_outText(VGAScreen, JE_fontCenter(specialName[7], TINY_FONT), 90, specialName[7], 12, 5);
  625.         JE_outText(VGAScreen, JE_fontCenter(miscText[64], TINY_FONT), 180, miscText[64], 15, 2);
  626.         JE_outText(VGAScreen, JE_fontCenter(miscText[65], TINY_FONT), 190, miscText[65], 15, 2);
  627.         JE_showVGA();
  628.         fade_palette(colors, 15, 0, 255);
  629.  
  630.         newkey = false;
  631.         while (!newkey)
  632.         {
  633.                 service_SDL_events(false);
  634.                 uSDL_Delay(16);
  635.         }
  636.  
  637.         fade_black(15);
  638.         memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
  639.         JE_showVGA();
  640. }
  641.  
  642. /* JE_modeSelect
  643.  *
  644.  * This function prints the DESTRUCT mode selection menu.
  645.  * The return value is the selected mode, or -1 (MODE_NONE)
  646.  * if the user quits.
  647.  */
  648. static void DrawModeSelectMenu( enum de_mode_t mode ) {
  649.  
  650.         int i;
  651.  
  652.         /* Helper function of JE_modeSelect.  Do not use elsewhere. */
  653.         for (i = 0; i < DESTRUCT_MODES; i++)
  654.         {   /* What a large function call. */
  655.                 JE_textShade(VGAScreen, JE_fontCenter(destructModeName[i], TINY_FONT), 82 + i * 12, destructModeName[i], 12, (i == mode) * 4, FULL_SHADE);
  656.         }
  657.         if (config.allow_custom == true)
  658.         {
  659.                 JE_textShade(VGAScreen, JE_fontCenter("Custom", TINY_FONT), 82 + i * 12, "Custom", 12, (i == mode) * 4, FULL_SHADE);
  660.         }
  661. }
  662. static enum de_mode_t JE_modeSelect( void )
  663. {
  664.         enum de_mode_t mode;
  665.  
  666.  
  667.         memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
  668.         mode = MODE_5CARDWAR;
  669.  
  670.         // Draw the menu and fade us in
  671.         DrawModeSelectMenu(mode);
  672.  
  673.         JE_showVGA();
  674.         fade_palette(colors, 15, 0, 255);
  675.  
  676.         /* Get input in a loop. */
  677.         while(1)
  678.         {
  679.                 /* Re-draw the menu every iteration */
  680.                 DrawModeSelectMenu(mode);
  681.                 JE_showVGA();
  682.  
  683.                 /* Grab keys */
  684.                 newkey = false;
  685.                 do {
  686.                         service_SDL_events(false);
  687.                         uSDL_Delay(16);
  688.                 } while(!newkey);
  689.  
  690.                 /* See what was pressed */
  691.                 if (keysactive[SDLK_ESCAPE])
  692.                 {
  693.                         mode = MODE_NONE; /* User is quitting, return failure */
  694.                         break;
  695.                 }
  696.                 if (keysactive[SDLK_RETURN])
  697.                 {
  698.                         break; /* User has selected, return choice */
  699.                 }
  700.                 if (keysactive[SDLK_UP])
  701.                 {
  702.                         if(mode == MODE_FIRST)
  703.                         {
  704.                                 if (config.allow_custom == true)
  705.                                 {
  706.                                         mode = MODE_LAST;
  707.                                 } else {
  708.                                         mode = MODE_LAST-1;
  709.                                 }
  710.                         } else {
  711.                                 mode--;
  712.                         }
  713.                 }
  714.                 if (keysactive[SDLK_DOWN])
  715.                 {
  716.                         if(mode >= MODE_LAST-1)
  717.                         {
  718.                                 if (config.allow_custom == true && mode == MODE_LAST-1)
  719.                                 {
  720.                                         mode++;
  721.                                 } else {
  722.                                         mode = MODE_FIRST;
  723.                                 }
  724.                         } else {
  725.                                 mode++;
  726.                         }
  727.                 }
  728.         }
  729.  
  730.         fade_black(15);
  731.         memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
  732.         JE_showVGA();
  733.         return(mode);
  734. }
  735.  
  736. static void JE_generateTerrain( void )
  737. {
  738.         /* The unique modifiers:
  739.             Altered generation (really tall)
  740.             Fuzzy hills
  741.             Rings of dirt
  742.  
  743.            The non-unique ones;:
  744.             Rings of not dirt (holes)
  745.             Walls
  746.         */
  747.  
  748.         world.mapFlags = MAP_NORMAL;
  749.  
  750.         if(mt_rand() % 2 == 0)
  751.         {
  752.                 world.mapFlags |= MAP_WALLS;
  753.         }
  754.         if(mt_rand() % 4 == 0)
  755.         {
  756.                 world.mapFlags |= MAP_HOLES;
  757.         }
  758.         switch(mt_rand() % 4)
  759.         {
  760.         case 0:
  761.                 world.mapFlags |= MAP_FUZZY;
  762.                 break;
  763.  
  764.         case 1:
  765.                 world.mapFlags |= MAP_TALL;
  766.                 break;
  767.  
  768.         case 2:
  769.                 world.mapFlags |= MAP_RINGS;
  770.                 break;
  771.         }
  772.  
  773.         play_song(goodsel[mt_rand() % 14] - 1);
  774.  
  775.         DE_generateBaseTerrain(world.mapFlags, world.baseMap);
  776.         DE_generateUnits(world.baseMap);
  777.         DE_generateWalls(&world);
  778.         DE_drawBaseTerrain(world.baseMap);
  779.  
  780.         if (world.mapFlags & MAP_RINGS)
  781.         {
  782.                 DE_generateRings(world.VGAScreen, PIXEL_DIRT);
  783.         }
  784.         if (world.mapFlags & MAP_HOLES)
  785.         {
  786.                 DE_generateRings(world.VGAScreen, PIXEL_BLACK);
  787.         }
  788.  
  789.         JE_aliasDirt(world.VGAScreen);
  790.         JE_showVGA();
  791.  
  792.         memcpy(destructTempScreen->pixels, VGAScreen->pixels, destructTempScreen->pitch * destructTempScreen->h);
  793. }
  794. static void DE_generateBaseTerrain( unsigned int mapFlags, unsigned int * baseWorld)
  795. {
  796.         unsigned int i;
  797.         unsigned int newheight, HeightMul;
  798.         float sinewave, sinewave2, cosinewave, cosinewave2;
  799.  
  800.  
  801.         /* The 'terrain' is actually the video buffer :).  If it's brown, flu... er,
  802.          * brown pixels are what we check for collisions with. */
  803.  
  804.         /* The ranges here are between .01 and roughly 0.07283...*/
  805.         sinewave    = mt_rand_lt1() * M_PI / 50 + 0.01f;
  806.         sinewave2   = mt_rand_lt1() * M_PI / 50 + 0.01f;
  807.         cosinewave  = mt_rand_lt1() * M_PI / 50 + 0.01f;
  808.         cosinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f;
  809.         HeightMul = 20;
  810.  
  811.         /* This block just exists to mix things up. */
  812.         if(mapFlags & MAP_FUZZY)
  813.         {
  814.                 sinewave  = M_PI - mt_rand_lt1() * 0.3f;
  815.                 sinewave2 = M_PI - mt_rand_lt1() * 0.3f;
  816.         }
  817.         if(mapFlags & MAP_TALL)
  818.         {
  819.                 HeightMul = 100;
  820.         }
  821.  
  822.         /* Now compute a height for each of our lines. */
  823.         for (i = 1; i <= 318; i++)
  824.         {
  825.                 newheight = roundf(sinf(sinewave   * i) * HeightMul + sinf(sinewave2   * i) * 15 +
  826.                                    cosf(cosinewave * i) * 10        + sinf(cosinewave2 * i) * 15) + 130;
  827.  
  828.                 /* Bind it; we have mins and maxs */
  829.                 if (newheight < 40)
  830.                 {
  831.                         newheight = 40;
  832.                 }
  833.                 else if (newheight > 195) {
  834.                         newheight = 195;
  835.                 }
  836.                 baseWorld[i] = newheight;
  837.         }
  838.         /* The base world has been created. */
  839. }
  840. static void DE_drawBaseTerrain( unsigned int * baseWorld)
  841. {
  842.         unsigned int i;
  843.  
  844.  
  845.         for (i = 1; i <= 318; i++)
  846.         {
  847.                 JE_rectangle(VGAScreen, i, baseWorld[i], i, 199, PIXEL_DIRT);
  848.         }
  849. }
  850.  
  851. static void DE_generateUnits( unsigned int * baseWorld )
  852. {
  853.         unsigned int i, j, numSatellites;
  854.  
  855.  
  856.         for (i = 0; i < MAX_PLAYERS; i++)
  857.         {
  858.                 numSatellites = 0;
  859.                 destruct_player[i].unitsRemaining = 0;
  860.  
  861.                 for (j = 0; j < basetypes[baseLookup[i][world.destructMode]][0]; j++)
  862.                 {
  863.                         /* Not everything is the same between players */
  864.                         if(i == PLAYER_LEFT)
  865.                         {
  866.                                 destruct_player[i].unit[j].unitX = (mt_rand() % 120) + 10;
  867.                         }
  868.                         else
  869.                         {
  870.                                 destruct_player[i].unit[j].unitX = 320 - ((mt_rand() % 120) + 22);
  871.                         }
  872.  
  873.                         destruct_player[i].unit[j].unitY = JE_placementPosition(destruct_player[i].unit[j].unitX - 1, 14, baseWorld);
  874.                         destruct_player[i].unit[j].unitType = basetypes[baseLookup[i][world.destructMode]][(mt_rand() % 10) + 1];
  875.  
  876.                         /* Sats are special cases since they are useless.  They don't count
  877.                          * as active units and we can't have a team of all sats */
  878.                         if (destruct_player[i].unit[j].unitType == UNIT_SATELLITE)
  879.                         {
  880.                                 if (numSatellites == basetypes[baseLookup[i][world.destructMode]][0])
  881.                                 {
  882.                                         destruct_player[i].unit[j].unitType = UNIT_TANK;
  883.                                         destruct_player[i].unitsRemaining++;
  884.                                 } else {
  885.                                         /* Place the satellite. Note: Earlier we cleared
  886.                                          * space with JE_placementPosition.  Now we are randomly
  887.                                          * placing the sat's Y.  It can be generated in hills
  888.                                          * and there is a clearing underneath it.  This CAN
  889.                                          * be fixed but won't be for classic.
  890.                                          */
  891.                                         destruct_player[i].unit[j].unitY = 30 + (mt_rand() % 40);
  892.                                         numSatellites++;
  893.                                 }
  894.                         }
  895.                         else
  896.                         {
  897.                                 destruct_player[i].unitsRemaining++;
  898.                         }
  899.  
  900.                         /* Now just fill in the rest of the unit's values. */
  901.                         destruct_player[i].unit[j].lastMove = 0;
  902.                         destruct_player[i].unit[j].unitYMov = 0;
  903.                         destruct_player[i].unit[j].isYInAir = false;
  904.                         destruct_player[i].unit[j].angle = 0;
  905.                         destruct_player[i].unit[j].power = (destruct_player[i].unit[j].unitType == UNIT_LASER) ? 6 : 3;
  906.                         destruct_player[i].unit[j].shotType = defaultWeapon[destruct_player[i].unit[j].unitType];
  907.                         destruct_player[i].unit[j].health = baseDamage[destruct_player[i].unit[j].unitType];
  908.                         destruct_player[i].unit[j].ani_frame = 0;
  909.                 }
  910.         }
  911. }
  912. static void DE_generateWalls( struct destruct_world_s * gameWorld )
  913. {
  914.         unsigned int i, j, wallX;
  915.         unsigned int wallHeight, remainWalls;
  916.         unsigned int tries;
  917.         bool isGood;
  918.  
  919.  
  920.         if ((world.mapFlags & MAP_WALLS) == false)
  921.         {
  922.                 /* Just clear them out */
  923.                 for (i = 0; i < config.max_walls; i++)
  924.                 {
  925.                         gameWorld->mapWalls[i].wallExist = false;
  926.                 }
  927.                 return;
  928.         }
  929.  
  930.         remainWalls = (rand() % (config.max_walls - config.min_walls + 1)) + config.min_walls;
  931.  
  932.         do {
  933.  
  934.                 /* Create a wall.  Decide how tall the wall will be */
  935.                 wallHeight = (mt_rand() % 5) + 1;
  936.                 if(wallHeight > remainWalls)
  937.                 {
  938.                         wallHeight = remainWalls;
  939.                 }
  940.  
  941.                 /* Now find a good place to put the wall. */
  942.                 tries = 0;
  943.                 do {
  944.  
  945.                         isGood = true;
  946.                         wallX = (mt_rand() % 300) + 10;
  947.  
  948.                         /* Is this X already occupied?  In the original Tyrian we only
  949.                          * checked to make sure four units on each side were unobscured.
  950.                          * That's not very scalable; instead I will check every unit,
  951.                          * but I'll only try plotting an unobstructed X four times.
  952.                          * After that we'll cover up what may; having a few units
  953.                          * stuck behind walls makes things mildly interesting.
  954.                          */
  955.                         for (i = 0; i < MAX_PLAYERS; i++)
  956.                         {
  957.                                 for (j = 0; j < config.max_installations; j++)
  958.                                 {
  959.                                         if ((wallX > destruct_player[i].unit[j].unitX - 12)
  960.                                          && (wallX < destruct_player[i].unit[j].unitX + 13))
  961.                                         {
  962.                                                 isGood = false;
  963.                                                 goto label_outer_break; /* I do feel that outer breaking is a legitimate goto use. */
  964.                                         }
  965.                                 }
  966.                         }
  967.  
  968. label_outer_break:
  969.                         tries++;
  970.  
  971.                 } while(isGood == false && tries < 5);
  972.  
  973.  
  974.                 /* We now have a valid X.  Create the wall. */
  975.                 for (i = 1; i <= wallHeight; i++)
  976.                 {
  977.                         gameWorld->mapWalls[remainWalls - i].wallExist = true;
  978.                         gameWorld->mapWalls[remainWalls - i].wallX = wallX;
  979.                         gameWorld->mapWalls[remainWalls - i].wallY = JE_placementPosition(wallX, 12, gameWorld->baseMap) - 14 * i;
  980.                 }
  981.  
  982.                 remainWalls -= wallHeight;
  983.  
  984.         } while (remainWalls != 0);
  985. }
  986.  
  987. static void DE_generateRings( SDL_Surface * screen, Uint8 pixel )
  988. {
  989.         unsigned int i, j, tempSize, rings;
  990.         int tempPosX1, tempPosY1, tempPosX2, tempPosY2;
  991.         float tempRadian;
  992.  
  993.  
  994.         rings = mt_rand() % 6 + 1;
  995.         for (i = 1; i <= rings; i++)
  996.         {
  997.                 tempPosX1 = (mt_rand() % 320);
  998.                 tempPosY1 = (mt_rand() % 160) + 20;
  999.                 tempSize = (mt_rand() % 40) + 10;  /*Size*/
  1000.  
  1001.                 for (j = 1; j <= tempSize * tempSize * 2; j++)
  1002.                 {
  1003.                         tempRadian = mt_rand_lt1() * (2 * M_PI);
  1004.                         tempPosY2 = tempPosY1 + roundf(cosf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize);
  1005.                         tempPosX2 = tempPosX1 + roundf(sinf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize);
  1006.                         if ((tempPosY2 > 12) && (tempPosY2 < 200)
  1007.                          && (tempPosX2 > 0) && (tempPosX2 < 319))
  1008.                         {
  1009.                                 ((Uint8 *)screen->pixels)[tempPosX2 + tempPosY2 * screen->pitch] = pixel;
  1010.                         }
  1011.                 }
  1012.         }
  1013. }
  1014.  
  1015. static unsigned int aliasDirtPixel(const SDL_Surface * screen, unsigned int x, unsigned int y, const Uint8 * s) {
  1016.  
  1017.         //A helper function used when aliasing dirt.  That's a messy process;
  1018.         //let's contain the mess here.
  1019.         unsigned int newColor = PIXEL_BLACK;
  1020.  
  1021.  
  1022.         if ((y > 0) && (*(s - screen->pitch) == PIXEL_DIRT)) { // look up
  1023.                 newColor += 1;
  1024.         }
  1025.         if ((y < screen->h - 1u) && (*(s + screen->pitch) == PIXEL_DIRT)) { // look down
  1026.                 newColor += 3;
  1027.         }
  1028.         if ((x > 0) && (*(s - 1) == PIXEL_DIRT)) { // look left
  1029.                 newColor += 2;
  1030.         }
  1031.         if ((x < screen->pitch - 1u) && (*(s + 1) == PIXEL_DIRT)) { // look right
  1032.                 newColor += 2;
  1033.         }
  1034.         if (newColor != PIXEL_BLACK) {
  1035.                 return(newColor + 16); // 16 must be the start of the brown pixels.
  1036.         }
  1037.  
  1038.         return(PIXEL_BLACK);
  1039. }
  1040. static void JE_aliasDirt( SDL_Surface * screen )
  1041. {
  1042.         /* This complicated looking function goes through the whole screen
  1043.          * looking for brown pixels which just happen to be next to non-brown
  1044.          * pixels.  It's an aliaser, just like it says. */
  1045.         unsigned int x, y;
  1046.  
  1047.  
  1048.         /* This is a pointer to a screen.  If you don't like pointer arithmetic,
  1049.          * you won't like this function. */
  1050.         Uint8 *s = screen->pixels;
  1051.         s += 12 * screen->pitch;
  1052.  
  1053.         for (y = 12; y < (unsigned)screen->h; y++) {
  1054.                 for (x = 0; x < screen->pitch; x++) {
  1055.                         if (*s == PIXEL_BLACK) {
  1056.                                 *s = aliasDirtPixel(screen, x, y, s);
  1057.                         }
  1058.  
  1059.                         s++;
  1060.                 }
  1061.         }
  1062. }
  1063.  
  1064. static unsigned int JE_placementPosition( unsigned int passed_x, unsigned int width, unsigned int * world )
  1065. {
  1066.         unsigned int i, new_y;
  1067.  
  1068.  
  1069.         /* This is the function responsible for carving out chunks of land.
  1070.          * There's a bug here, but it's a pretty major gameplay altering one:
  1071.          * areas can be carved out for units that are aerial or in mountains.
  1072.          * This can result in huge caverns.  Ergo, it's a feature :)
  1073.          *
  1074.          * I wondered if it might be better to not carve out land at all.
  1075.          * On testing I determined that was distracting and added nothing. */
  1076.         new_y = 0;
  1077.         for (i = passed_x; i <= passed_x + width - 1; i++)
  1078.         {
  1079.                 if (new_y < world[i])
  1080.                         new_y = world[i];
  1081.         }
  1082.  
  1083.         for (i = passed_x; i <= passed_x + width - 1; i++)
  1084.         {
  1085.                 world[i] = new_y;
  1086.         }
  1087.  
  1088.         return new_y;
  1089. }
  1090.  
  1091. static bool JE_stabilityCheck( unsigned int x, unsigned int y )
  1092. {
  1093.         unsigned int i, numDirtPixels;
  1094.         Uint8 * s;
  1095.  
  1096.  
  1097.         numDirtPixels = 0;
  1098.         s = destructTempScreen->pixels;
  1099.         s += x + (y * destructTempScreen->pitch) - 1;
  1100.  
  1101.         /* Check the 12 pixels on the bottom border of our object */
  1102.         for (i = 0; i < 12; i++)
  1103.         {
  1104.                 if (*s == PIXEL_DIRT)
  1105.                         numDirtPixels++;
  1106.  
  1107.                 s++;
  1108.         }
  1109.  
  1110.         /* If there are fewer than 10 brown pixels we don't consider it a solid base */
  1111.         return (numDirtPixels < 10);
  1112. }
  1113.  
  1114. static void JE_tempScreenChecking( void ) /*and copy to vgascreen*/
  1115. {
  1116.         Uint8 *s = VGAScreen->pixels;
  1117.         s += 12 * VGAScreen->pitch;
  1118.  
  1119.         Uint8 *temps = destructTempScreen->pixels;
  1120.         temps += 12 * destructTempScreen->pitch;
  1121.  
  1122.         for (int y = 12; y < VGAScreen->h; y++)
  1123.         {
  1124.                 for (int x = 0; x < VGAScreen->pitch; x++)
  1125.                 {
  1126.                         // This block is what fades out explosions. The palette from 241
  1127.                         // to 255 fades from a very dark red to a very bright yellow.
  1128.                         if (*temps >= 241)
  1129.                         {
  1130.                                 if (*temps == 241)
  1131.                                         *temps = PIXEL_BLACK;
  1132.                                 else
  1133.                                         (*temps)--;
  1134.                         }
  1135.  
  1136.                         // This block is for aliasing dirt.  Computers are fast these days,
  1137.                         // and it's fun.
  1138.                         if (config.alwaysalias == true && *temps == PIXEL_BLACK) {
  1139.                                 *temps = aliasDirtPixel(VGAScreen, x, y, temps);
  1140.                         }
  1141.  
  1142.                         /* This is copying from our temp screen to VGAScreen */
  1143.                         *s = *temps;
  1144.  
  1145.                         s++;
  1146.                         temps++;
  1147.                 }
  1148.         }
  1149. }
  1150.  
  1151. static void JE_makeExplosion( unsigned int tempPosX, unsigned int tempPosY, enum de_shot_t shottype )
  1152. {
  1153.         unsigned int i, tempExploSize;
  1154.  
  1155.  
  1156.         /* First find an open explosion. If we can't find one, return.*/
  1157.         for (i = 0; i < config.max_explosions; i++)
  1158.         {
  1159.                 if (exploRec[i].isAvailable == true)
  1160.                         break;
  1161.         }
  1162.         if (i == config.max_explosions) /* No empty slots */
  1163.         {
  1164.                 return;
  1165.         }
  1166.  
  1167.  
  1168.         exploRec[i].isAvailable = false;
  1169.         exploRec[i].x = tempPosX;
  1170.         exploRec[i].y = tempPosY;
  1171.         exploRec[i].explowidth = 2;
  1172.  
  1173.         if(shottype != SHOT_INVALID)
  1174.         {
  1175.                 tempExploSize = exploSize[shottype];
  1176.                 if (tempExploSize < 5)
  1177.                         JE_eSound(3);
  1178.                 else if (tempExploSize < 15)
  1179.                         JE_eSound(4);
  1180.                 else if (tempExploSize < 20)
  1181.                         JE_eSound(12);
  1182.                 else if (tempExploSize < 40)
  1183.                         JE_eSound(11);
  1184.                 else
  1185.                 {
  1186.                         JE_eSound(12);
  1187.                         JE_eSound(11);
  1188.                 }
  1189.  
  1190.                 exploRec[i].explomax  = tempExploSize;
  1191.                 exploRec[i].explofill = exploDensity[shottype];
  1192.                 exploRec[i].exploType = shotDirt[shottype];
  1193.         }
  1194.         else
  1195.         {
  1196.                 JE_eSound(4);
  1197.                 exploRec[i].explomax  = (mt_rand() % 40) + 10;
  1198.                 exploRec[i].explofill = (mt_rand() % 60) + 20;
  1199.                 exploRec[i].exploType = EXPL_NORMAL;
  1200.         }
  1201. }
  1202.  
  1203. static void JE_eSound( unsigned int sound )
  1204. {
  1205.         static int exploSoundChannel = 0;
  1206.  
  1207.         if (++exploSoundChannel > 5)
  1208.         {
  1209.                 exploSoundChannel = 1;
  1210.         }
  1211.  
  1212.         soundQueue[exploSoundChannel] = sound;
  1213. }
  1214.  
  1215. static void JE_superPixel( unsigned int tempPosX, unsigned int tempPosY )
  1216. {
  1217.         const unsigned int starPattern[5][5] = {
  1218.                 {   0,   0, 246,   0,   0 },
  1219.                 {   0, 247, 249, 247,   0 },
  1220.                 { 246, 249, 252, 249, 246 },
  1221.                 {   0, 247, 249, 247,   0 },
  1222.                 {   0,   0, 246,   0,   0 }
  1223.         };
  1224.         const unsigned int starIntensity[5][5] = {
  1225.                 {   0,   0,   1,   0,   0 },
  1226.                 {   0,   1,   2,   1,   0 },
  1227.                 {   1,   2,   4,   2,   1 },
  1228.                 {   0,   1,   2,   1,   0 },
  1229.                 {   0,   0,   1,   0,   0 }
  1230.         };
  1231.  
  1232.         int x, y, maxX, maxY;
  1233.         unsigned int rowLen;
  1234.         Uint8 *s;
  1235.  
  1236.  
  1237.         maxX = destructTempScreen->pitch;
  1238.         maxY = destructTempScreen->h;
  1239.  
  1240.         rowLen = destructTempScreen->pitch;
  1241.         s = destructTempScreen->pixels;
  1242.         s += (rowLen * (tempPosY - 2)) + (tempPosX - 2);
  1243.  
  1244.         for (y = 0; y < 5; y++, s += rowLen - 5)
  1245.         {
  1246.                 if ((signed)tempPosY + y - 2 < 0     /* would be out of bounds */
  1247.                 ||  (signed)tempPosY + y - 2 >= maxY) { continue; }
  1248.  
  1249.                 for (x = 0; x < 5; x++, s++)
  1250.                 {
  1251.                         if ((signed)tempPosX + x - 2 < 0
  1252.                          || (signed)tempPosX + x - 2 >= maxX) { continue; }
  1253.  
  1254.                         if (starPattern[y][x] == 0) { continue; } /* this is just to speed it up */
  1255.  
  1256.                         /* at this point *s is our pixel.  Our constant arrays tell us what
  1257.                          * to do with it. */
  1258.                         if (*s < starPattern[y][x])
  1259.                         {
  1260.                                 *s = starPattern[y][x];
  1261.                         }
  1262.                         else if (*s + starIntensity[y][x] > 255)
  1263.                         {
  1264.                                 *s = 255;
  1265.                         }
  1266.                         else
  1267.                         {
  1268.                                 *s += starIntensity[y][x];
  1269.                         }
  1270.                 }
  1271.         }
  1272. }
  1273.  
  1274. static void JE_helpScreen( void )
  1275. {
  1276.         unsigned int i, j;
  1277.  
  1278.  
  1279.         //JE_getVGA();  didn't do anything anyway?
  1280.         fade_black(15);
  1281.         memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
  1282.         JE_clr256(VGAScreen);
  1283.  
  1284.         for(i = 0; i < 2; i++)
  1285.         {
  1286.                 JE_outText(VGAScreen, 100,  5 + i * 90, destructHelp[i * 12 + 0], 2, 4);
  1287.                 JE_outText(VGAScreen, 100, 15 + i * 90, destructHelp[i * 12 + 1], 2, 1);
  1288.                 for (j = 3; j <= 12; j++)
  1289.                 {
  1290.                         JE_outText(VGAScreen, ((j - 1) % 2) * 160 + 10, 15 + ((j - 1) / 2) * 12 + i * 90, destructHelp[i * 12 + j-1], 1, 3);
  1291.                 }
  1292.         }
  1293.         JE_outText(VGAScreen, 30, 190, destructHelp[24], 3, 4);
  1294.         JE_showVGA();
  1295.         fade_palette(colors, 15, 0, 255);
  1296.  
  1297.         do  /* wait until user hits a key */
  1298.         {
  1299.                 service_SDL_events(true);
  1300.                 uSDL_Delay(16);
  1301.         }
  1302.         while (!newkey);
  1303.  
  1304.         fade_black(15);
  1305.         memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
  1306.         JE_showVGA();
  1307.         fade_palette(colors, 15, 0, 255);
  1308. }
  1309.  
  1310.  
  1311. static void JE_pauseScreen( void )
  1312. {
  1313.         set_volume(tyrMusicVolume / 2, fxVolume);
  1314.  
  1315.         /* Save our current screen/game world.  We don't want to screw it up while paused. */
  1316.         memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
  1317.         JE_outText(VGAScreen, JE_fontCenter(miscText[22], TINY_FONT), 90, miscText[22], 12, 5);
  1318.         JE_showVGA();
  1319.  
  1320.         do  /* wait until user hits a key */
  1321.         {
  1322.                 service_SDL_events(true);
  1323.                 uSDL_Delay(16);
  1324.         }
  1325.         while (!newkey);
  1326.  
  1327.         /* Restore current screen & volume*/
  1328.         memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
  1329.         JE_showVGA();
  1330.  
  1331.         set_volume(tyrMusicVolume, fxVolume);
  1332. }
  1333.  
  1334. /* DE_ResetX
  1335.  *
  1336.  * The reset functions clear the state of whatefer they are assigned to.
  1337.  */
  1338. static void DE_ResetUnits( void )
  1339. {
  1340.         unsigned int p, u;
  1341.  
  1342.  
  1343.         for (p = 0; p < MAX_PLAYERS; ++p)
  1344.                 for (u = 0; u < config.max_installations; ++u)
  1345.                         destruct_player[p].unit[u].health = 0;
  1346. }
  1347. static void DE_ResetPlayers( void )
  1348. {
  1349.         unsigned int i;
  1350.  
  1351.  
  1352.         for (i = 0; i < MAX_PLAYERS; ++i)
  1353.         {
  1354.                 destruct_player[i].is_cpu = false;
  1355.                 destruct_player[i].unitSelected = 0;
  1356.                 destruct_player[i].shotDelay = 0;
  1357.                 destruct_player[i].score = 0;
  1358.                 destruct_player[i].aiMemory.c_Angle = 0;
  1359.                 destruct_player[i].aiMemory.c_Power = 0;
  1360.                 destruct_player[i].aiMemory.c_Fire = 0;
  1361.                 destruct_player[i].aiMemory.c_noDown = 0;
  1362.                 memcpy(destruct_player[i].keys.Config, defaultKeyConfig[i], sizeof(destruct_player[i].keys.Config));
  1363.         }
  1364. }
  1365. static void DE_ResetWeapons( void )
  1366. {
  1367.         unsigned int i;
  1368.  
  1369.  
  1370.         for (i = 0; i < config.max_shots; i++)
  1371.                 shotRec[i].isAvailable = true;
  1372.  
  1373.         for (i = 0; i < config.max_explosions; i++)
  1374.                 exploRec[i].isAvailable = true;
  1375. }
  1376. static void DE_ResetLevel( void )
  1377. {
  1378.         /* Okay, let's prep the arena */
  1379.  
  1380.         DE_ResetWeapons();
  1381.  
  1382.         JE_generateTerrain();
  1383.         DE_ResetAI();
  1384. }
  1385. static void DE_ResetAI( void )
  1386. {
  1387.         unsigned int i, j;
  1388.         struct destruct_unit_s * ptr;
  1389.  
  1390.  
  1391.         for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++)
  1392.         {
  1393.                 if (destruct_player[i].is_cpu == false) { continue; }
  1394.                 ptr = destruct_player[i].unit;
  1395.  
  1396.                 for( j = 0; j < config.max_installations; j++, ptr++)
  1397.                 {
  1398.                         if(DE_isValidUnit(ptr) == false)
  1399.                                 continue;
  1400.  
  1401.                         if (systemAngle[ptr->unitType] || ptr->unitType == UNIT_HELI)
  1402.                                 ptr->angle = M_PI_4;
  1403.                         else
  1404.                                 ptr->angle = 0;
  1405.  
  1406.                         ptr->power = (ptr->unitType == UNIT_LASER) ? 6 : 4;
  1407.  
  1408.                         if (world.mapFlags & MAP_WALLS)
  1409.                                 ptr->shotType = defaultCpuWeaponB[ptr->unitType];
  1410.                         else
  1411.                                 ptr->shotType = defaultCpuWeapon[ptr->unitType];
  1412.                 }
  1413.         }
  1414. }
  1415. static void DE_ResetActions( void )
  1416. {
  1417.         unsigned int i;
  1418.  
  1419.  
  1420.         for(i = 0; i < MAX_PLAYERS; i++)
  1421.         {       /* Zero it all.  A memset would do the trick */
  1422.                 memset(&(destruct_player[i].moves), 0, sizeof(destruct_player[i].moves));
  1423.         }
  1424. }
  1425. /* DE_RunTick
  1426.  *
  1427.  * Runs one tick.  One tick involves handling physics, drawing crap,
  1428.  * moving projectiles and explosions, and getting input.
  1429.  * Returns true while the game is running or false if the game is
  1430.  * to be terminated.
  1431.  */
  1432. static enum de_state_t DE_RunTick( void )
  1433. {
  1434.         static unsigned int endDelay;
  1435.  
  1436.  
  1437.         setjasondelay(1);
  1438.  
  1439.         memset(soundQueue, 0, sizeof(soundQueue));
  1440.         JE_tempScreenChecking();
  1441.  
  1442.         DE_ResetActions();
  1443.         DE_RunTickCycleDeadUnits();
  1444.  
  1445.  
  1446.         DE_RunTickGravity();
  1447.         DE_RunTickAnimate();
  1448.         DE_RunTickDrawWalls();
  1449.         DE_RunTickExplosions();
  1450.         DE_RunTickShots();
  1451.         DE_RunTickAI();
  1452.         DE_RunTickDrawCrosshairs();
  1453.         DE_RunTickDrawHUD();
  1454.         JE_showVGA();
  1455.  
  1456.         if (destructFirstTime)
  1457.         {
  1458.                 fade_palette(colors, 25, 0, 255);
  1459.                 destructFirstTime = false;
  1460.                 endDelay = 0;
  1461.         }
  1462.  
  1463.         DE_RunTickGetInput();
  1464.         DE_ProcessInput();
  1465.  
  1466.         if (endDelay > 0)
  1467.         {
  1468.                 if(--endDelay == 0)
  1469.                 {
  1470.                         return(STATE_RELOAD);
  1471.                 }
  1472.         }
  1473.         else if ( DE_RunTickCheckEndgame() == true)
  1474.         {
  1475.                 endDelay = 80;
  1476.         }
  1477.  
  1478.         DE_RunTickPlaySounds();
  1479.  
  1480.         /* The rest of this cruft needs to be put in appropriate sections */
  1481.         if (keysactive[SDLK_F10])
  1482.         {
  1483.                 destruct_player[PLAYER_LEFT].is_cpu = !destruct_player[PLAYER_LEFT].is_cpu;
  1484.                 keysactive[SDLK_F10] = false;
  1485.         }
  1486.         if (keysactive[SDLK_F11])
  1487.         {
  1488.                 destruct_player[PLAYER_RIGHT].is_cpu = !destruct_player[PLAYER_RIGHT].is_cpu;
  1489.                 keysactive[SDLK_F11] = false;
  1490.         }
  1491.         if (keysactive[SDLK_p])
  1492.         {
  1493.                 JE_pauseScreen();
  1494.                 keysactive[lastkey_sym] = false;
  1495.         }
  1496.  
  1497.         if (keysactive[SDLK_F1])
  1498.         {
  1499.                 JE_helpScreen();
  1500.                 keysactive[lastkey_sym] = false;
  1501.         }
  1502.  
  1503.         wait_delay();
  1504.  
  1505.         if (keysactive[SDLK_ESCAPE])
  1506.         {
  1507.                 keysactive[SDLK_ESCAPE] = false;
  1508.                 return(STATE_INIT); /* STATE_INIT drops us to the mode select */
  1509.         }
  1510.  
  1511.         if (keysactive[SDLK_BACKSPACE])
  1512.         {
  1513.                 keysactive[SDLK_BACKSPACE] = false;
  1514.                 return(STATE_RELOAD); /* STATE_RELOAD creates a new map */
  1515.         }
  1516.  
  1517.         return(STATE_CONTINUE);
  1518. }
  1519.  
  1520. /* DE_RunTickX
  1521.  *
  1522.  * Handles something that we do once per tick, such as
  1523.  * track ammo and move asplosions.
  1524.  */
  1525. static void DE_RunTickCycleDeadUnits( void )
  1526. {
  1527.         unsigned int i;
  1528.         struct destruct_unit_s * unit;
  1529.  
  1530.  
  1531.         /* This code automatically switches the active unit if it is destroyed
  1532.          * and skips over the useless satellite */
  1533.         for (i = 0; i < MAX_PLAYERS; i++)
  1534.         {
  1535.                 if (destruct_player[i].unitsRemaining == 0) { continue; }
  1536.  
  1537.                 unit = &(destruct_player[i].unit[destruct_player[i].unitSelected]);
  1538.                 while(DE_isValidUnit(unit) == false
  1539.                    || unit->shotType == SHOT_INVALID)
  1540.                 {
  1541.                         destruct_player[i].unitSelected++;
  1542.                         unit++;
  1543.                         if (destruct_player[i].unitSelected >= config.max_installations)
  1544.                         {
  1545.                                 destruct_player[i].unitSelected = 0;
  1546.                                 unit = destruct_player[i].unit;
  1547.                         }
  1548.                 }
  1549.         }
  1550. }
  1551. static void DE_RunTickGravity( void )
  1552. {
  1553.         unsigned int i, j;
  1554.         struct destruct_unit_s * unit;
  1555.  
  1556.  
  1557.         for (i = 0; i < MAX_PLAYERS; i++)
  1558.         {
  1559.  
  1560.                 unit = destruct_player[i].unit;
  1561.                 for (j = 0; j < config.max_installations; j++, unit++)
  1562.                 {
  1563.                         if (DE_isValidUnit(unit) == false) /* invalid unit */
  1564.                                 continue;
  1565.  
  1566.                         switch(unit->unitType)
  1567.                         {
  1568.                         case UNIT_SATELLITE: /* satellites don't fall down */
  1569.                                 break;
  1570.  
  1571.                         case UNIT_HELI:
  1572.                         case UNIT_JUMPER:
  1573.                                 if (unit->isYInAir == true) /* unit is falling down, at least in theory */
  1574.                                 {
  1575.                                         DE_GravityFlyUnit(unit);
  1576.                                         break;
  1577.                                 }
  1578.                                 /* else treat as a normal unit */
  1579.                                 /* fall through */
  1580.                         default:
  1581.                                 DE_GravityLowerUnit(unit);
  1582.                         }
  1583.  
  1584.                 /* Draw the unit. */
  1585.                 DE_GravityDrawUnit(i, unit);
  1586.                 }
  1587.         }
  1588. }
  1589. static void DE_GravityDrawUnit( enum de_player_t team, struct destruct_unit_s * unit )
  1590. {
  1591.         unsigned int anim_index;
  1592.  
  1593.  
  1594.         anim_index = GraphicBase[team][unit->unitType] + unit->ani_frame;
  1595.         if (unit->unitType == UNIT_HELI)
  1596.         {
  1597.                 /* Adjust animation index if we are travelling right or left. */
  1598.                 if (unit->lastMove < -2)
  1599.                         anim_index += 5;
  1600.                 else if (unit->lastMove > 2)
  1601.                         anim_index += 10;
  1602.         }
  1603.         else /* This handles our cannons and the like */
  1604.         {
  1605.                 anim_index += floorf(unit->angle * 9.99f / M_PI);
  1606.         }
  1607.  
  1608.         blit_sprite2(VGAScreen, unit->unitX, roundf(unit->unitY) - 13, eShapes[0], anim_index);
  1609. }
  1610. static void DE_GravityLowerUnit( struct destruct_unit_s * unit )
  1611. {
  1612.         /* units fall at a constant speed.  The heli is an odd case though;
  1613.          * we simply give it a downward velocity, but due to a buggy implementation
  1614.          * the chopper didn't lower until you tried to fly it up.  Tyrian 2000 fixes
  1615.          * this by not making the chopper a special case.  I've decided to actually
  1616.          * mix both; the chopper is given a slight downward acceleration (simulating
  1617.          * a 'rocky' takeoff), and it is lowered like a regular unit, but not as
  1618.          * quickly.
  1619.          */
  1620.         if(unit->unitY < 199) { /* checking takes time, don't check if it's at the bottom */
  1621.                 if (JE_stabilityCheck(unit->unitX, roundf(unit->unitY)))
  1622.                 {
  1623.                         switch(unit->unitType)
  1624.                         {
  1625.                         case UNIT_HELI:
  1626.                                 unit->unitYMov = 1.5f;
  1627.                                 unit->unitY += 0.2f;
  1628.                                 break;
  1629.  
  1630.                         default:
  1631.                                 unit->unitY += 1;
  1632.                         }
  1633.  
  1634.                         if (unit->unitY > 199) /* could be possible */
  1635.                                 unit->unitY = 199;
  1636.                 }
  1637.         }
  1638. }
  1639. static void DE_GravityFlyUnit( struct destruct_unit_s * unit )
  1640. {
  1641.         if (unit->unitY + unit->unitYMov > 199) /* would hit bottom of screen */
  1642.         {
  1643.                 unit->unitY = 199;
  1644.                 unit->unitYMov = 0;
  1645.                 unit->isYInAir = false;
  1646.                 return;
  1647.         }
  1648.  
  1649.         /* move the unit and alter acceleration */
  1650.         unit->unitY += unit->unitYMov;
  1651.         if (unit->unitY < 24) /* This stops units from going above the screen */
  1652.         {
  1653.                 unit->unitYMov = 0;
  1654.                 unit->unitY = 24;
  1655.         }
  1656.  
  1657.         if (unit->unitType == UNIT_HELI) /* helicopters fall more slowly */
  1658.                 unit->unitYMov += 0.0001f;
  1659.         else
  1660.                 unit->unitYMov += 0.03f;
  1661.  
  1662.         if (!JE_stabilityCheck(unit->unitX, roundf(unit->unitY)))
  1663.         {
  1664.                 unit->unitYMov = 0;
  1665.                 unit->isYInAir = false;
  1666.         }
  1667. }
  1668. static void DE_RunTickAnimate( void )
  1669. {
  1670.         unsigned int p, u;
  1671.         struct destruct_unit_s * ptr;
  1672.  
  1673.  
  1674.         for (p = 0; p < MAX_PLAYERS; ++p)
  1675.         {
  1676.                 ptr = destruct_player[p].unit;
  1677.                 for (u = 0; u < config.max_installations; ++u,  ++ptr)
  1678.                 {
  1679.                         /* Don't mess with any unit that is unallocated
  1680.                          * or doesn't animate and is set to frame 0 */
  1681.                         if(DE_isValidUnit(ptr) == false) { continue; }
  1682.                         if(systemAni[ptr->unitType] == false && ptr->ani_frame == 0) { continue; }
  1683.  
  1684.                         if (++(ptr->ani_frame) > 3)
  1685.                         {
  1686.                                 ptr->ani_frame = 0;
  1687.                         }
  1688.                 }
  1689.         }
  1690. }
  1691. static void DE_RunTickDrawWalls( void )
  1692. {
  1693.         unsigned int i;
  1694.  
  1695.  
  1696.         for (i = 0; i < config.max_walls; i++)
  1697.         {
  1698.                 if (world.mapWalls[i].wallExist)
  1699.                 {
  1700.                         blit_sprite2(VGAScreen, world.mapWalls[i].wallX, world.mapWalls[i].wallY, eShapes[0], 42);
  1701.                 }
  1702.         }
  1703. }
  1704. static void DE_RunTickExplosions( void )
  1705. {
  1706.         unsigned int i, j;
  1707.         int tempPosX, tempPosY;
  1708.         float tempRadian;
  1709.  
  1710.  
  1711.         /* Run through all open explosions.  They are not sorted in any way */
  1712.         for (i = 0; i < config.max_explosions; i++)
  1713.         {
  1714.                 if (exploRec[i].isAvailable == true) { continue; } /* Nothing to do */
  1715.  
  1716.                 for (j = 0; j < exploRec[i].explofill; j++)
  1717.                 {
  1718.                         /* An explosion is comprised of multiple 'flares' that fan out.
  1719.                            Calculate where this 'flare' will end up */
  1720.                         tempRadian = mt_rand_lt1() * (2 * M_PI);
  1721.                         tempPosY = exploRec[i].y + roundf(cosf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth);
  1722.                         tempPosX = exploRec[i].x + roundf(sinf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth);
  1723.  
  1724.                         /* Our game allows explosions to wrap around.  This looks to have
  1725.                          * originally been a bug that was left in as being fun, but we are
  1726.                          * going to replicate it w/o risking out of bound arrays. */
  1727.  
  1728.                         while(tempPosX < 0)   { tempPosX += 320; }
  1729.                         while(tempPosX > 320) { tempPosX -= 320; }
  1730.  
  1731.                         /* We don't draw our explosion if it's out of bounds vertically */
  1732.                         if (tempPosY >= 200 || tempPosY <= 15) { continue; }
  1733.  
  1734.                         /* And now the drawing.  There are only two types of explosions
  1735.                          * right now; dirt and flares.  Dirt simply draws a brown pixel;
  1736.                          * flares explode and have a star formation. */
  1737.                         switch(exploRec[i].exploType)
  1738.                         {
  1739.                                 case EXPL_DIRT:
  1740.                                         ((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch] = PIXEL_DIRT;
  1741.                                         break;
  1742.  
  1743.                                 case EXPL_NORMAL:
  1744.                                         JE_superPixel(tempPosX, tempPosY);
  1745.                                         DE_TestExplosionCollision(tempPosX, tempPosY);
  1746.                                         break;
  1747.  
  1748.                                 default:
  1749.                                         assert(false);
  1750.                                         break;
  1751.                         }
  1752.                 }
  1753.  
  1754.                 /* Widen the explosion and delete it if necessary. */
  1755.                 exploRec[i].explowidth++;
  1756.                 if (exploRec[i].explowidth == exploRec[i].explomax)
  1757.                 {
  1758.                         exploRec[i].isAvailable = true;
  1759.                 }
  1760.         }
  1761. }
  1762. static void DE_TestExplosionCollision( unsigned int PosX, unsigned int PosY)
  1763. {
  1764.         unsigned int i, j;
  1765.         struct destruct_unit_s * unit;
  1766.  
  1767.  
  1768.         for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++)
  1769.         {
  1770.                 unit = destruct_player[i].unit;
  1771.                 for (j = 0; j < config.max_installations; j++, unit++)
  1772.                 {
  1773.                         if (DE_isValidUnit(unit) == true
  1774.                          && PosX > unit->unitX && PosX < unit->unitX + 11
  1775.                          && PosY < unit->unitY && PosY > unit->unitY - 11)
  1776.                         {
  1777.                                 unit->health--;
  1778.                                 if (unit->health <= 0)
  1779.                                 {
  1780.                                         DE_DestroyUnit(i, unit);
  1781.                                 }
  1782.                         }
  1783.                 }
  1784.         }
  1785. }
  1786. static void DE_DestroyUnit( enum de_player_t playerID, struct destruct_unit_s * unit )
  1787. {
  1788.         /* This function call was an evil evil piece of brilliance before.  Go on.
  1789.          * Look at the older revisions.  It passed the result of a comparison.
  1790.          * MULTIPLIED.  This is at least a little clearer... */
  1791.         JE_makeExplosion(unit->unitX + 5, roundf(unit->unitY) - 5, (unit->unitType == UNIT_HELI) ? SHOT_SMALL : SHOT_INVALID); /* Helicopters explode like small shots do.  Invalids are their own special case. */
  1792.  
  1793.         if (unit->unitType != UNIT_SATELLITE) /* increment score */
  1794.         { /* todo: change when teams are created. Hacky kludge for now.*/
  1795.                 destruct_player[playerID].unitsRemaining--;
  1796.                 destruct_player[((playerID == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT)].score++;
  1797.         }
  1798. }
  1799.  
  1800. static void DE_RunTickShots( void )
  1801. {
  1802.         unsigned int i, j, k;
  1803.         unsigned int tempTrails;
  1804.         unsigned int tempPosX, tempPosY;
  1805.         struct destruct_unit_s * unit;
  1806.  
  1807.  
  1808.         for (i = 0; i < config.max_shots; i++)
  1809.         {
  1810.                 if (shotRec[i].isAvailable == true) { continue; } /* Nothing to do */
  1811.  
  1812.                 /* Move the shot.  Simple displacement */
  1813.                 shotRec[i].x += shotRec[i].xmov;
  1814.                 shotRec[i].y += shotRec[i].ymov;
  1815.  
  1816.                 /* If the shot can bounce off the map, bounce it */
  1817.                 if (shotBounce[shotRec[i].shottype])
  1818.                 {
  1819.                         if (shotRec[i].y > 199 || shotRec[i].y < 14)
  1820.                         {
  1821.                                 shotRec[i].y -= shotRec[i].ymov;
  1822.                                 shotRec[i].ymov = -shotRec[i].ymov;
  1823.                         }
  1824.                         if (shotRec[i].x < 1 || shotRec[i].x > 318)
  1825.                         {
  1826.                                 shotRec[i].x -= shotRec[i].xmov;
  1827.                                 shotRec[i].xmov = -shotRec[i].xmov;
  1828.                         }
  1829.                 }
  1830.                 else /* If it cannot, apply normal physics */
  1831.                 {
  1832.                         shotRec[i].ymov += 0.05f; /* add gravity */
  1833.  
  1834.                         if (shotRec[i].y > 199) /* We hit the floor */
  1835.                         {
  1836.                                 shotRec[i].y -= shotRec[i].ymov;
  1837.                                 shotRec[i].ymov = -shotRec[i].ymov * 0.8f; /* bounce at reduced velocity */
  1838.  
  1839.                                 /* Don't allow a bouncing shot to bounce straight up and down */
  1840.                                 if (shotRec[i].xmov == 0)
  1841.                                 {
  1842.                                         shotRec[i].xmov += mt_rand_lt1() - 0.5f;
  1843.                                 }
  1844.                         }
  1845.                 }
  1846.  
  1847.                 /* Shot has gone out of bounds. Eliminate it. */
  1848.                 if (shotRec[i].x > 318 || shotRec[i].x < 1)
  1849.                 {
  1850.                         shotRec[i].isAvailable = true;
  1851.                         continue;
  1852.                 }
  1853.  
  1854.                 /* Now check for collisions. */
  1855.  
  1856.                 /* Don't bother checking for collisions above the map :) */
  1857.                 if (shotRec[i].y <= 14)
  1858.                         continue;
  1859.  
  1860.                 tempPosX = roundf(shotRec[i].x);
  1861.                 tempPosY = roundf(shotRec[i].y);
  1862.  
  1863.                 /*Check building hits*/
  1864.                 for(j = 0; j < MAX_PLAYERS; j++)
  1865.                 {
  1866.                         unit = destruct_player[j].unit;
  1867.                         for(k = 0; k < config.max_installations; k++, unit++)
  1868.                         {
  1869.                                 if (DE_isValidUnit(unit) == false)
  1870.                                         continue;
  1871.  
  1872.                                 if (tempPosX > unit->unitX && tempPosX < unit->unitX + 11
  1873.                                  && tempPosY < unit->unitY && tempPosY > unit->unitY - 13)
  1874.                                 {
  1875.                                         shotRec[i].isAvailable = true;
  1876.                                         JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype);
  1877.                                 }
  1878.                         }
  1879.                 }
  1880.  
  1881.                 tempTrails = (shotColor[shotRec[i].shottype] << 4) - 3;
  1882.                 JE_pixCool(tempPosX, tempPosY, tempTrails);
  1883.  
  1884.                 /*Draw the shot trail (if applicable) */
  1885.                 switch (shotTrail[shotRec[i].shottype])
  1886.                 {
  1887.                 case TRAILS_NONE:
  1888.                         break;
  1889.                 case TRAILS_NORMAL:
  1890.                         DE_DrawTrails( &(shotRec[i]), 2, 4, tempTrails - 3 );
  1891.                         break;
  1892.                 case TRAILS_FULL:
  1893.                         DE_DrawTrails( &(shotRec[i]), 4, 3, tempTrails - 1 );
  1894.                         break;
  1895.                 }
  1896.  
  1897.                 /* Bounce off of or destroy walls */
  1898.                 for (j = 0; j < config.max_walls; j++)
  1899.                 {
  1900.                         if (world.mapWalls[j].wallExist == true
  1901.                          && tempPosX >= world.mapWalls[j].wallX && tempPosX <= world.mapWalls[j].wallX + 11
  1902.                          && tempPosY >= world.mapWalls[j].wallY && tempPosY <= world.mapWalls[j].wallY + 14)
  1903.                         {
  1904.                                 if (demolish[shotRec[i].shottype])
  1905.                                 {
  1906.                                         /* Blow up the wall and remove the shot. */
  1907.                                         world.mapWalls[j].wallExist = false;
  1908.                                         shotRec[i].isAvailable = true;
  1909.                                         JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype);
  1910.                                         continue;
  1911.                                 }
  1912.                                 else
  1913.                                 {
  1914.                                         /* Otherwise, bounce. */
  1915.                                         if (shotRec[i].x - shotRec[i].xmov < world.mapWalls[j].wallX
  1916.                                          || shotRec[i].x - shotRec[i].xmov > world.mapWalls[j].wallX + 11)
  1917.                                         {
  1918.                                                 shotRec[i].xmov = -shotRec[i].xmov;
  1919.                                         }
  1920.                                         if (shotRec[i].y - shotRec[i].ymov < world.mapWalls[j].wallY
  1921.                                          || shotRec[i].y - shotRec[i].ymov > world.mapWalls[j].wallY + 14)
  1922.                                         {
  1923.                                                 if (shotRec[i].ymov < 0)
  1924.                                                         shotRec[i].ymov = -shotRec[i].ymov;
  1925.                                                 else
  1926.                                                         shotRec[i].ymov = -shotRec[i].ymov * 0.8f;
  1927.                                         }
  1928.  
  1929.                                         tempPosX = roundf(shotRec[i].x);
  1930.                                         tempPosY = roundf(shotRec[i].y);
  1931.                                 }
  1932.                         }
  1933.                 }
  1934.  
  1935.                 /* Our last collision check, at least for now.  We hit dirt. */
  1936.                 if((((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch]) == PIXEL_DIRT)
  1937.                 {
  1938.                         shotRec[i].isAvailable = true;
  1939.                         JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype);
  1940.                         continue;
  1941.                 }
  1942.         }
  1943. }
  1944. static void DE_DrawTrails( struct destruct_shot_s * shot, unsigned int count, unsigned int decay, unsigned int startColor )
  1945. {
  1946.         int i;
  1947.  
  1948.  
  1949.         for (i = count-1; i >= 0; i--) /* going in reverse is important as it affects how we draw */
  1950.         {
  1951.                 if (shot->trailc[i] > 0 && shot->traily[i] > 12) /* If it exists and if it's not out of bounds, draw it. */
  1952.                 {
  1953.                         JE_pixCool(shot->trailx[i], shot->traily[i], shot->trailc[i]);
  1954.                 }
  1955.  
  1956.                 if (i == 0) /* The first trail we create. */
  1957.                 {
  1958.                         shot->trailx[i] = roundf(shot->x);
  1959.                         shot->traily[i] = roundf(shot->y);
  1960.                         shot->trailc[i] = startColor;
  1961.                 }
  1962.                 else /* The newer trails decay into the older trails.*/
  1963.                 {
  1964.                         shot->trailx[i] = shot->trailx[i-1];
  1965.                         shot->traily[i] = shot->traily[i-1];
  1966.                         if (shot->trailc[i-1] > 0)
  1967.                         {
  1968.                                 shot->trailc[i] = shot->trailc[i-1] - decay;
  1969.                         }
  1970.                 }
  1971.         }
  1972. }
  1973. static void DE_RunTickAI( void )
  1974. {
  1975.         unsigned int i, j;
  1976.         struct destruct_player_s * ptrPlayer, * ptrTarget;
  1977.         struct destruct_unit_s * ptrUnit, * ptrCurUnit;
  1978.  
  1979.  
  1980.         for (i = 0; i < MAX_PLAYERS; i++)
  1981.         {
  1982.                 ptrPlayer = &(destruct_player[i]);
  1983.                 if (ptrPlayer->is_cpu == false)
  1984.                 {
  1985.                         continue;
  1986.                 }
  1987.  
  1988.  
  1989.                 /* I've been thinking, purely hypothetically, about what it would take
  1990.                  * to have multiple computer opponents.  The answer?  A lot of crap
  1991.                  * and a 'target' variable in the destruct_player struct. */
  1992.                 j = i + 1;
  1993.                 if (j >= MAX_PLAYERS)
  1994.                 {
  1995.                         j = 0;
  1996.                 }
  1997.  
  1998.                 ptrTarget  = &(destruct_player[j]);
  1999.                 ptrCurUnit = &(ptrPlayer->unit[ptrPlayer->unitSelected]);
  2000.  
  2001.  
  2002.                 /* This is the start of the original AI.  Heh.  AI. */
  2003.  
  2004.                 if (ptrPlayer->aiMemory.c_noDown > 0)
  2005.                         ptrPlayer->aiMemory.c_noDown--;
  2006.  
  2007.                 /* Until all structs are properly divvied up this must only apply to player1 */
  2008.                 if (mt_rand() % 100 > 80)
  2009.                 {
  2010.                         ptrPlayer->aiMemory.c_Angle += (mt_rand() % 3) - 1;
  2011.  
  2012.                         if (ptrPlayer->aiMemory.c_Angle > 1)
  2013.                                 ptrPlayer->aiMemory.c_Angle = 1;
  2014.                         else
  2015.                         if (ptrPlayer->aiMemory.c_Angle < -1)
  2016.                                 ptrPlayer->aiMemory.c_Angle = -1;
  2017.                 }
  2018.                 if (mt_rand() % 100 > 90)
  2019.                 {
  2020.                         if (ptrPlayer->aiMemory.c_Angle > 0 && ptrCurUnit->angle > (M_PI_2) - (M_PI / 9))
  2021.                                 ptrPlayer->aiMemory.c_Angle = 0;
  2022.                         else
  2023.                         if (ptrPlayer->aiMemory.c_Angle < 0 && ptrCurUnit->angle < M_PI / 8)
  2024.                                 ptrPlayer->aiMemory.c_Angle = 0;
  2025.                 }
  2026.  
  2027.                 if (mt_rand() % 100 > 93)
  2028.                 {
  2029.                         ptrPlayer->aiMemory.c_Power += (mt_rand() % 3) - 1;
  2030.  
  2031.                         if (ptrPlayer->aiMemory.c_Power > 1)
  2032.                                 ptrPlayer->aiMemory.c_Power = 1;
  2033.                         else
  2034.                         if (ptrPlayer->aiMemory.c_Power < -1)
  2035.                                 ptrPlayer->aiMemory.c_Power = -1;
  2036.                 }
  2037.                 if (mt_rand() % 100 > 90)
  2038.                 {
  2039.                         if (ptrPlayer->aiMemory.c_Power > 0 && ptrCurUnit->power > 4)
  2040.                                 ptrPlayer->aiMemory.c_Power = 0;
  2041.                         else
  2042.                         if (ptrPlayer->aiMemory.c_Power < 0 && ptrCurUnit->power < 3)
  2043.                                 ptrPlayer->aiMemory.c_Power = 0;
  2044.                         else
  2045.                         if (ptrCurUnit->power < 2)
  2046.                                 ptrPlayer->aiMemory.c_Power = 1;
  2047.                 }
  2048.  
  2049.                 // prefer helicopter
  2050.                 ptrUnit = ptrPlayer->unit;
  2051.                 for (j = 0; j < config.max_installations; j++, ptrUnit++)
  2052.                 {
  2053.                         if (DE_isValidUnit(ptrUnit) && ptrUnit->unitType == UNIT_HELI)
  2054.                         {
  2055.                                 ptrPlayer->unitSelected = j;
  2056.                                 break;
  2057.                         }
  2058.                 }
  2059.  
  2060.                 if (ptrCurUnit->unitType == UNIT_HELI)
  2061.                 {
  2062.                         if (ptrCurUnit->isYInAir == false)
  2063.                         {
  2064.                                 ptrPlayer->aiMemory.c_Power = 1;
  2065.                         }
  2066.                         if (mt_rand() % ptrCurUnit->unitX > 100)
  2067.                         {
  2068.                                 ptrPlayer->aiMemory.c_Power = 1;
  2069.                         }
  2070.                         if (mt_rand() % 240 > ptrCurUnit->unitX)
  2071.                         {
  2072.                                 ptrPlayer->moves.actions[MOVE_RIGHT] = true;
  2073.                         }
  2074.                         else if ((mt_rand() % 20) + 300 < ptrCurUnit->unitX)
  2075.                         {
  2076.                                 ptrPlayer->moves.actions[MOVE_LEFT] = true;
  2077.                         }
  2078.                         else if (mt_rand() % 30 == 1)
  2079.                         {
  2080.                                 ptrPlayer->aiMemory.c_Angle = (mt_rand() % 3) - 1;
  2081.                         }
  2082.                         if (ptrCurUnit->unitX > 295 && ptrCurUnit->lastMove > 1)
  2083.                         {
  2084.                                 ptrPlayer->moves.actions[MOVE_LEFT] = true;
  2085.                                 ptrPlayer->moves.actions[MOVE_RIGHT] = false;
  2086.                         }
  2087.                         if (ptrCurUnit->unitType != UNIT_HELI || ptrCurUnit->lastMove > 3 || (ptrCurUnit->unitX > 160 && ptrCurUnit->lastMove > -3))
  2088.                         {
  2089.                                 if (mt_rand() % (int)roundf(ptrCurUnit->unitY) < 150 && ptrCurUnit->unitYMov < 0.01f && (ptrCurUnit->unitX < 160 || ptrCurUnit->lastMove < 2))
  2090.                                 {
  2091.                                         ptrPlayer->moves.actions[MOVE_FIRE] = true;
  2092.                                 }
  2093.                                 ptrPlayer->aiMemory.c_noDown = (5 - abs(ptrCurUnit->lastMove)) * (5 - abs(ptrCurUnit->lastMove)) + 3;
  2094.                                 ptrPlayer->aiMemory.c_Power = 1;
  2095.                         } else {
  2096.                                 ptrPlayer->moves.actions[MOVE_FIRE] = false;
  2097.                         }
  2098.  
  2099.                         ptrUnit = ptrTarget->unit;
  2100.                         for (j = 0; j < config.max_installations; j++, ptrUnit++)
  2101.                         {
  2102.                                 if (abs(ptrUnit->unitX - ptrCurUnit->unitX) < 8)
  2103.                                 {
  2104.                                         /* I get it.  This makes helicoptors hover over
  2105.                                          * their enemies. */
  2106.                                         if (ptrUnit->unitType == UNIT_SATELLITE)
  2107.                                         {
  2108.                                                 ptrPlayer->moves.actions[MOVE_FIRE] = false;
  2109.                                         }
  2110.                                         else
  2111.                                         {
  2112.                                                 ptrPlayer->moves.actions[MOVE_LEFT] = false;
  2113.                                                 ptrPlayer->moves.actions[MOVE_RIGHT] = false;
  2114.                                                 if (ptrCurUnit->lastMove < -1)
  2115.                                                 {
  2116.                                                         ptrCurUnit->lastMove++;
  2117.                                                 }
  2118.                                                 else if (ptrCurUnit->lastMove > 1)
  2119.                                                 {
  2120.                                                         ptrCurUnit->lastMove--;
  2121.                                                 }
  2122.                                         }
  2123.                                 }
  2124.                         }
  2125.                 } else {
  2126.                         ptrPlayer->moves.actions[MOVE_FIRE] = 1;
  2127.                 }
  2128.  
  2129.                 if (mt_rand() % 200 > 198)
  2130.                 {
  2131.                         ptrPlayer->moves.actions[MOVE_CHANGE] = true;
  2132.                         ptrPlayer->aiMemory.c_Angle = 0;
  2133.                         ptrPlayer->aiMemory.c_Power = 0;
  2134.                         ptrPlayer->aiMemory.c_Fire = 0;
  2135.                 }
  2136.  
  2137.                 if (mt_rand() % 100 > 98 || ptrCurUnit->shotType == SHOT_TRACER)
  2138.                 {   /* Clearly the CPU doesn't like the tracer :) */
  2139.                         ptrPlayer->moves.actions[MOVE_CYDN] = true;
  2140.                 }
  2141.                 if (ptrPlayer->aiMemory.c_Angle > 0)
  2142.                 {
  2143.                         ptrPlayer->moves.actions[MOVE_LEFT] = true;
  2144.                 }
  2145.                 if (ptrPlayer->aiMemory.c_Angle < 0)
  2146.                 {
  2147.                         ptrPlayer->moves.actions[MOVE_RIGHT] = true;
  2148.                 }
  2149.                 if (ptrPlayer->aiMemory.c_Power > 0)
  2150.                 {
  2151.                         ptrPlayer->moves.actions[MOVE_UP] = true;
  2152.                 }
  2153.                 if (ptrPlayer->aiMemory.c_Power < 0 && ptrPlayer->aiMemory.c_noDown == 0)
  2154.                 {
  2155.                         ptrPlayer->moves.actions[MOVE_DOWN] = true;
  2156.                 }
  2157.                 if (ptrPlayer->aiMemory.c_Fire > 0)
  2158.                 {
  2159.                         ptrPlayer->moves.actions[MOVE_FIRE] = true;
  2160.                 }
  2161.  
  2162.                 if (ptrCurUnit->unitYMov < -0.1f && ptrCurUnit->unitType == UNIT_HELI)
  2163.                 {
  2164.                         ptrPlayer->moves.actions[MOVE_FIRE] = false;
  2165.                 }
  2166.  
  2167.                 /* This last hack was down in the processing section.
  2168.                  * What exactly it was doing there I do not know */
  2169.                 if(ptrCurUnit->unitType == UNIT_LASER || ptrCurUnit->isYInAir == true) {
  2170.                         ptrPlayer->aiMemory.c_Power = 0;
  2171.                 }
  2172.         }
  2173. }
  2174. static void DE_RunTickDrawCrosshairs( void )
  2175. {
  2176.         unsigned int i;
  2177.         int tempPosX, tempPosY;
  2178.         int direction;
  2179.         struct destruct_unit_s * curUnit;
  2180.  
  2181.  
  2182.         /* Draw the crosshairs.  Most vehicles aim left or right.  Helis can aim
  2183.          * either way and this must be accounted for.
  2184.          */
  2185.         for (i = 0; i < MAX_PLAYERS; i++)
  2186.         {
  2187.                 direction = (i == PLAYER_LEFT) ? -1 : 1;
  2188.                 curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]);
  2189.  
  2190.                 if (curUnit->unitType == UNIT_HELI)
  2191.                 {
  2192.                         tempPosX = curUnit->unitX + roundf(0.1f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove) + 5;
  2193.                         tempPosY = roundf(curUnit->unitY) + 1;
  2194.                 } else {
  2195.                         tempPosX = roundf(curUnit->unitX + 6 - cosf(curUnit->angle) * (curUnit->power * 8 + 7) * direction);
  2196.                         tempPosY = roundf(curUnit->unitY - 7 - sinf(curUnit->angle) * (curUnit->power * 8 + 7));
  2197.                 }
  2198.  
  2199.                 /* Draw it.  Clip away from the HUD though. */
  2200.                 if(tempPosY > 9)
  2201.                 {
  2202.                         if(tempPosY > 11)
  2203.                         {
  2204.                                 if(tempPosY > 13)
  2205.                                 {
  2206.                                         /* Top pixel */
  2207.                                         JE_pix(VGAScreen, tempPosX,     tempPosY - 2,  3);
  2208.                                 }
  2209.                                 /* Middle three pixels */
  2210.                                 JE_pix(VGAScreen, tempPosX + 3, tempPosY,      3);
  2211.                                 JE_pix(VGAScreen, tempPosX,     tempPosY,     14);
  2212.                                 JE_pix(VGAScreen, tempPosX - 3, tempPosY,      3);
  2213.                         }
  2214.                         /* Bottom pixel */
  2215.                         JE_pix(VGAScreen, tempPosX,     tempPosY + 2,  3);
  2216.                 }
  2217.         }
  2218. }
  2219. static void DE_RunTickDrawHUD( void )
  2220. {
  2221.         unsigned int i;
  2222.         unsigned int startX;
  2223.         char tempstr[16]; /* Max size needed: 16 assuming 10 digit int max. */
  2224.         struct destruct_unit_s * curUnit;
  2225.  
  2226.  
  2227.         for (i = 0; i < MAX_PLAYERS; i++)
  2228.         {
  2229.                 curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]);
  2230.                 startX = ((i == PLAYER_LEFT) ? 0 : 320 - 150);
  2231.  
  2232.                 fill_rectangle_xy(VGAScreen, startX +  5, 3, startX +  14, 8, 241);
  2233.                 JE_rectangle(VGAScreen, startX +  4, 2, startX +  15, 9, 242);
  2234.                 JE_rectangle(VGAScreen, startX +  3, 1, startX +  16, 10, 240);
  2235.                 fill_rectangle_xy(VGAScreen, startX + 18, 3, startX + 140, 8, 241);
  2236.                 JE_rectangle(VGAScreen, startX + 17, 2, startX + 143, 9, 242);
  2237.                 JE_rectangle(VGAScreen, startX + 16, 1, startX + 144, 10, 240);
  2238.  
  2239.                 blit_sprite2(VGAScreen, startX +  4, 0, eShapes[0], 191 + curUnit->shotType);
  2240.  
  2241.                 JE_outText   (VGAScreen, startX + 20, 3, weaponNames[curUnit->shotType], 15, 2);
  2242.                 sprintf      (tempstr, "dmg~%d~", curUnit->health);
  2243.                 JE_outText   (VGAScreen, startX + 75, 3, tempstr, 15, 0);
  2244.                 sprintf      (tempstr, "pts~%d~", destruct_player[i].score);
  2245.                 JE_outText   (VGAScreen, startX + 110, 3, tempstr, 15, 0);
  2246.         }
  2247. }
  2248. static void DE_RunTickGetInput( void )
  2249. {
  2250.         unsigned int player_index, key_index, slot_index;
  2251.         SDLKey key;
  2252.  
  2253.         /* destruct_player.keys holds our key config.  Players will eventually be
  2254.          * allowed to can change their key mappings.  destruct_player.moves and
  2255.          * destruct_player.keys line up; rather than manually checking left and
  2256.          * right we can just loop through the indexes and set the actions as
  2257.          * needed. */
  2258.         service_SDL_events(true);
  2259.  
  2260.         for(player_index = 0; player_index < MAX_PLAYERS; player_index++)
  2261.         {
  2262.                 for(key_index = 0; key_index < MAX_KEY; key_index++)
  2263.                 {
  2264.                         for(slot_index = 0; slot_index < MAX_KEY_OPTIONS; slot_index++)
  2265.                         {
  2266.                                 key = destruct_player[player_index].keys.Config[key_index][slot_index];
  2267.                                 if(key == SDLK_UNKNOWN) { break; }
  2268.                                 if(keysactive[key] == true)
  2269.                                 {
  2270.                                         /* The right key was clearly pressed */
  2271.                                         destruct_player[player_index].moves.actions[key_index] = true;
  2272.  
  2273.                                         /* Some keys we want to toggle afterwards */
  2274.                                         if(key_index == KEY_CHANGE ||
  2275.                                            key_index == KEY_CYUP   ||
  2276.                                            key_index == KEY_CYDN)
  2277.                                         {
  2278.                                                 keysactive[key] = false;
  2279.                                         }
  2280.                                         break;
  2281.                                 }
  2282.                         }
  2283.                 }
  2284.         }
  2285. }
  2286. static void DE_ProcessInput( void )
  2287. {
  2288.         int direction;
  2289.  
  2290.         unsigned int player_index;
  2291.         struct destruct_unit_s * curUnit;
  2292.  
  2293.  
  2294.         for (player_index = 0; player_index < MAX_PLAYERS; player_index++)
  2295.         {
  2296.                 if (destruct_player[player_index].unitsRemaining <= 0) { continue; }
  2297.  
  2298.                 direction = (player_index == PLAYER_LEFT) ? -1 : 1;
  2299.                 curUnit = &(destruct_player[player_index].unit[destruct_player[player_index].unitSelected]);
  2300.  
  2301.                 if (systemAngle[curUnit->unitType] == true) /* selected unit may change shot angle */
  2302.                 {
  2303.                         if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true)
  2304.                         {
  2305.                                 (player_index == PLAYER_LEFT) ? DE_RaiseAngle(curUnit) : DE_LowerAngle(curUnit);
  2306.                         }
  2307.                         if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true)
  2308.                         {
  2309.                                 (player_index == PLAYER_LEFT) ? DE_LowerAngle(curUnit) : DE_RaiseAngle(curUnit);
  2310.  
  2311.                         }
  2312.                 } else if (curUnit->unitType == UNIT_HELI) {
  2313.                         if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true && curUnit->unitX > 5)
  2314.                                 if (JE_stabilityCheck(curUnit->unitX - 5, roundf(curUnit->unitY)))
  2315.                                 {
  2316.                                         if (curUnit->lastMove > -5)
  2317.                                         {
  2318.                                                 curUnit->lastMove--;
  2319.                                         }
  2320.                                         curUnit->unitX--;
  2321.                                         if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY)))
  2322.                                         {
  2323.                                                 curUnit->isYInAir = true;
  2324.                                         }
  2325.                                 }
  2326.                         if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true && curUnit->unitX < 305)
  2327.                         {
  2328.                                 if (JE_stabilityCheck(curUnit->unitX + 5, roundf(curUnit->unitY)))
  2329.                                 {
  2330.                                         if (curUnit->lastMove < 5)
  2331.                                         {
  2332.                                                 curUnit->lastMove++;
  2333.                                         }
  2334.                                         curUnit->unitX++;
  2335.                                         if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY)))
  2336.                                         {
  2337.                                                 curUnit->isYInAir = true;
  2338.                                         }
  2339.                                 }
  2340.                         }
  2341.                 }
  2342.  
  2343.                 if (curUnit->unitType != UNIT_LASER)
  2344.  
  2345.                 {       /*increasepower*/
  2346.                         if (destruct_player[player_index].moves.actions[MOVE_UP] == true)
  2347.                         {
  2348.                                 if (curUnit->unitType == UNIT_HELI)
  2349.                                 {
  2350.                                         curUnit->isYInAir = true;
  2351.                                         curUnit->unitYMov -= 0.1f;
  2352.                                 }
  2353.                                 else if (curUnit->unitType == UNIT_JUMPER
  2354.                                       && curUnit->isYInAir == false) {
  2355.                                         curUnit->unitYMov = -3;
  2356.                                         curUnit->isYInAir = true;
  2357.                                 }
  2358.                                 else {
  2359.                                         DE_RaisePower(curUnit);
  2360.                                 }
  2361.                         }
  2362.                         /*decreasepower*/
  2363.                         if (destruct_player[player_index].moves.actions[MOVE_DOWN] == true)
  2364.                         {
  2365.                                 if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == true)
  2366.                                 {
  2367.                                         curUnit->unitYMov += 0.1f;
  2368.                                 } else {
  2369.                                         DE_LowerPower(curUnit);
  2370.                                 }
  2371.                         }
  2372.                 }
  2373.  
  2374.                 /*up/down weapon.  These just cycle until a valid weapon is found */
  2375.                 if (destruct_player[player_index].moves.actions[MOVE_CYUP] == true)
  2376.                 {
  2377.                         DE_CycleWeaponUp(curUnit);
  2378.                 }
  2379.                 if (destruct_player[player_index].moves.actions[MOVE_CYDN] == true)
  2380.                 {
  2381.                         DE_CycleWeaponDown(curUnit);
  2382.                 }
  2383.  
  2384.                 /* Change.  Since change would change out curUnit pointer, let's just do it last.
  2385.                  * Validity checking is performed at the beginning of the tick. */
  2386.                 if (destruct_player[player_index].moves.actions[MOVE_CHANGE] == true)
  2387.                 {
  2388.                         destruct_player[player_index].unitSelected++;
  2389.                         if (destruct_player[player_index].unitSelected >= config.max_installations)
  2390.                         {
  2391.                                 destruct_player[player_index].unitSelected = 0;
  2392.                         }
  2393.                 }
  2394.  
  2395.                 /*Newshot*/
  2396.                 if (destruct_player[player_index].shotDelay > 0)
  2397.                 {
  2398.                         destruct_player[player_index].shotDelay--;
  2399.                 }
  2400.                 if (destruct_player[player_index].moves.actions[MOVE_FIRE] == true
  2401.                 && (destruct_player[player_index].shotDelay == 0))
  2402.                 {
  2403.                         destruct_player[player_index].shotDelay = shotDelay[curUnit->shotType];
  2404.  
  2405.                         switch(shotDirt[curUnit->shotType])
  2406.                         {
  2407.                                 case EXPL_NONE:
  2408.                                         break;
  2409.  
  2410.                                 case EXPL_MAGNET:
  2411.                                         DE_RunMagnet(player_index, curUnit);
  2412.                                         break;
  2413.  
  2414.                                 case EXPL_DIRT:
  2415.                                 case EXPL_NORMAL:
  2416.                                         DE_MakeShot(player_index, curUnit, direction);
  2417.                                         break;
  2418.  
  2419.                                 default:
  2420.                                         assert(false);
  2421.                         }
  2422.                 }
  2423.         }
  2424. }
  2425.  
  2426. static void DE_CycleWeaponUp( struct destruct_unit_s * unit )
  2427. {
  2428.         do
  2429.         {
  2430.                 unit->shotType++;
  2431.                 if (unit->shotType > SHOT_LAST)
  2432.                 {
  2433.                         unit->shotType = SHOT_FIRST;
  2434.                 }
  2435.         } while (weaponSystems[unit->unitType][unit->shotType] == 0);
  2436. }
  2437. static void DE_CycleWeaponDown( struct destruct_unit_s * unit )
  2438. {
  2439.         do
  2440.         {
  2441.                 unit->shotType--;
  2442.                 if (unit->shotType < SHOT_FIRST)
  2443.                 {
  2444.                         unit->shotType = SHOT_LAST;
  2445.                 }
  2446.         } while (weaponSystems[unit->unitType][unit->shotType] == 0);
  2447. }
  2448.  
  2449.  
  2450. static void DE_MakeShot( enum de_player_t curPlayer, const struct destruct_unit_s * curUnit, int direction )
  2451. {
  2452.         unsigned int i;
  2453.         unsigned int shotIndex;
  2454.  
  2455.  
  2456.         /* First, find an empty shot struct we can use */
  2457.         for (i = 0; ; i++)
  2458.         {
  2459.                 if (i >= config.max_shots) { return; } /* no empty slots.  Do nothing. */
  2460.  
  2461.                 if (shotRec[i].isAvailable)
  2462.                 {
  2463.                         shotIndex = i;
  2464.                         break;
  2465.                 }
  2466.         }
  2467.         if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == false)
  2468.         { /* Helis can't fire when they are on the ground. */
  2469.                 return;
  2470.         }
  2471.  
  2472.         /* Play the firing sound */
  2473.         soundQueue[curPlayer] = shotSound[curUnit->shotType];
  2474.  
  2475.         /* Create our shot.  Some units have differing logic here */
  2476.         switch (curUnit->unitType)
  2477.         {
  2478.                 case UNIT_HELI:
  2479.  
  2480.                         shotRec[shotIndex].x = curUnit->unitX + curUnit->lastMove * 2 + 5;
  2481.                         shotRec[shotIndex].xmov = 0.02f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove;
  2482.  
  2483.                         /* If we are trying in vain to move up off the screen, act differently.*/
  2484.                         if (destruct_player[curPlayer].moves.actions[MOVE_UP] && curUnit->unitY < 30)
  2485.                         {
  2486.                                 shotRec[shotIndex].y = curUnit->unitY;
  2487.                                 shotRec[shotIndex].ymov = 0.1f;
  2488.  
  2489.                                 if (shotRec[shotIndex].xmov < 0)
  2490.                                 {
  2491.                                         shotRec[shotIndex].xmov += 0.1f;
  2492.                                 }
  2493.                                 else if (shotRec[shotIndex].xmov > 0)
  2494.                                 {
  2495.                                         shotRec[shotIndex].xmov -= 0.1f;
  2496.                                 }
  2497.                         }
  2498.                         else
  2499.                         {
  2500.                                 shotRec[shotIndex].y = curUnit->unitY + 1;
  2501.                                 shotRec[shotIndex].ymov = 0.5f + curUnit->unitYMov * 0.1f;
  2502.                         }
  2503.                         break;
  2504.  
  2505.                 case UNIT_JUMPER: /* Jumpers are normally only special for the left hand player.  Bug?  Or feature? */
  2506.  
  2507.                         if(config.jumper_straight[curPlayer])
  2508.                         {
  2509.                                 /* This is identical to the default case.
  2510.                                  * I considered letting the switch fall through
  2511.                                  * but that's more confusing to people who aren't used
  2512.                                  * to that quirk of switch. */
  2513.  
  2514.                                 shotRec[shotIndex].x    = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction;
  2515.                                 shotRec[shotIndex].y    = curUnit->unitY - 7 - sinf(curUnit->angle) * 10;
  2516.                                 shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction;
  2517.                                 shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power;
  2518.                         }
  2519.                         else
  2520.                         {
  2521.                                 /* This is not identical to the default case. */
  2522.  
  2523.                                 shotRec[shotIndex].x = curUnit->unitX + 2;
  2524.                                 shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction;
  2525.  
  2526.                                 if (curUnit->isYInAir == true)
  2527.                                 {
  2528.                                         shotRec[shotIndex].ymov = 1;
  2529.                                         shotRec[shotIndex].y = curUnit->unitY + 2;
  2530.                                 } else {
  2531.                                         shotRec[shotIndex].ymov = -2;
  2532.                                         shotRec[shotIndex].y = curUnit->unitY - 12;
  2533.                                 }
  2534.                         }
  2535.                         break;
  2536.  
  2537.                 default:
  2538.  
  2539.                         shotRec[shotIndex].x    = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction;
  2540.                         shotRec[shotIndex].y    = curUnit->unitY - 7 - sinf(curUnit->angle) * 10;
  2541.                         shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction;
  2542.                         shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power;
  2543.                         break;
  2544.         }
  2545.  
  2546.         /* Now set/clear out a few last details. */
  2547.         shotRec[shotIndex].isAvailable = false;
  2548.  
  2549.         shotRec[shotIndex].shottype = curUnit->shotType;
  2550.         //shotRec[shotIndex].shotdur = shotFuse[shotRec[shotIndex].shottype];
  2551.  
  2552.         shotRec[shotIndex].trailc[0] = 0;
  2553.         shotRec[shotIndex].trailc[1] = 0;
  2554.         shotRec[shotIndex].trailc[2] = 0;
  2555.         shotRec[shotIndex].trailc[3] = 0;
  2556. }
  2557. static void DE_RunMagnet( enum de_player_t curPlayer, struct destruct_unit_s * magnet )
  2558. {
  2559.         unsigned int i;
  2560.         enum de_player_t curEnemy;
  2561.         int direction;
  2562.         struct destruct_unit_s * enemyUnit;
  2563.  
  2564.  
  2565.         curEnemy = (curPlayer == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT;
  2566.         direction = (curPlayer == PLAYER_LEFT) ? -1 : 1;
  2567.  
  2568.         /* Push all shots that are in front of the magnet */
  2569.         for (i = 0; i < config.max_shots; i++)
  2570.         {
  2571.                 if (shotRec[i].isAvailable == false)
  2572.                 {
  2573.                         if ((curPlayer == PLAYER_LEFT  && shotRec[i].x > magnet->unitX)
  2574.                          || (curPlayer == PLAYER_RIGHT && shotRec[i].x < magnet->unitX))
  2575.                         {
  2576.                                 shotRec[i].xmov += magnet->power * 0.1f * -direction;
  2577.                         }
  2578.                 }
  2579.         }
  2580.  
  2581.         enemyUnit = destruct_player[curEnemy].unit;
  2582.         for (i = 0; i < config.max_installations; i++, enemyUnit++) /* magnets push coptors */
  2583.         {
  2584.                 if (DE_isValidUnit(enemyUnit)
  2585.                  && enemyUnit->unitType == UNIT_HELI
  2586.                  && enemyUnit->isYInAir == true)
  2587.                 {
  2588.                         if ((curEnemy == PLAYER_RIGHT && destruct_player[curEnemy].unit[i].unitX + 11 < 318)
  2589.                          || (curEnemy == PLAYER_LEFT  && destruct_player[curEnemy].unit[i].unitX > 1))
  2590.                         {
  2591.                                 enemyUnit->unitX -= 2 * direction;
  2592.                         }
  2593.                 }
  2594.         }
  2595.         magnet->ani_frame = 1;
  2596. }
  2597. static void DE_RaiseAngle( struct destruct_unit_s * unit )
  2598. {
  2599.         unit->angle += 0.01f;
  2600.         if (unit->angle > M_PI_2 - 0.01f)
  2601.         {
  2602.                 unit->angle = M_PI_2 - 0.01f;
  2603.         }
  2604. }
  2605. static void DE_LowerAngle( struct destruct_unit_s * unit )
  2606. {
  2607.         unit->angle -= 0.01f;
  2608.         if (unit->angle < 0)
  2609.         {
  2610.                 unit->angle = 0;
  2611.         }
  2612. }
  2613. static void DE_RaisePower( struct destruct_unit_s * unit )
  2614. {
  2615.         unit->power += 0.05f;
  2616.         if (unit->power > 5)
  2617.         {
  2618.         unit->power = 5;
  2619.         }
  2620. }
  2621. static void DE_LowerPower( struct destruct_unit_s * unit )
  2622. {
  2623.         unit->power -= 0.05f;
  2624.         if (unit->power < 1)
  2625.         {
  2626.                 unit->power = 1;
  2627.         }
  2628. }
  2629.  
  2630. /* DE_isValidUnit
  2631.  *
  2632.  * Returns true if the unit's health is above 0 and false
  2633.  * otherwise.  This mainly exists because the 'health' var
  2634.  * serves two roles and that can get confusing.
  2635.  */
  2636. static inline bool DE_isValidUnit( struct destruct_unit_s * unit )
  2637. {
  2638.         return(unit->health > 0);
  2639. }
  2640.  
  2641.  
  2642. static bool DE_RunTickCheckEndgame( void )
  2643. {
  2644.         if (destruct_player[PLAYER_LEFT].unitsRemaining == 0)
  2645.         {
  2646.                 destruct_player[PLAYER_RIGHT].score += ModeScore[PLAYER_LEFT][world.destructMode];
  2647.                 soundQueue[7] = V_CLEARED_PLATFORM;
  2648.                 return(true);
  2649.         }
  2650.         if (destruct_player[PLAYER_RIGHT].unitsRemaining == 0)
  2651.         {
  2652.                 destruct_player[PLAYER_LEFT].score += ModeScore[PLAYER_RIGHT][world.destructMode];
  2653.                 soundQueue[7] = V_CLEARED_PLATFORM;
  2654.                 return(true);
  2655.         }
  2656.         return(false);
  2657. }
  2658. static void DE_RunTickPlaySounds( void )
  2659. {
  2660.         unsigned int i, tempSampleIndex, tempVolume;
  2661.  
  2662.  
  2663.         for (i = 0; i < COUNTOF(soundQueue); i++)
  2664.         {
  2665.                 if (soundQueue[i] != S_NONE)
  2666.                 {
  2667.                         tempSampleIndex = soundQueue[i];
  2668.                         if (i == 7)
  2669.                         {
  2670.                                 tempVolume = fxPlayVol;
  2671.                         }
  2672.                         else
  2673.                         {
  2674.                                 tempVolume = fxPlayVol / 2;
  2675.                         }
  2676.  
  2677.                         JE_multiSamplePlay(digiFx[tempSampleIndex-1], fxSize[tempSampleIndex-1], i, tempVolume);
  2678.                         soundQueue[i] = S_NONE;
  2679.                 }
  2680.         }
  2681. }
  2682.  
  2683. static void JE_pixCool( unsigned int x, unsigned int y, Uint8 c )
  2684. {
  2685.         JE_pix(VGAScreen, x, y, c);
  2686.         JE_pix(VGAScreen, x - 1, y, c - 2);
  2687.         JE_pix(VGAScreen, x + 1, y, c - 2);
  2688.         JE_pix(VGAScreen, x, y - 1, c - 2);
  2689.         JE_pix(VGAScreen, x, y + 1, c - 2);
  2690. }
  2691.