2
* OpenTyrian: A modern cross-platform port of Tyrian
3
* Copyright (C) 2007-2009 The OpenTyrian Development Team
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.
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.
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.
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.
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
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.
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.
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...
68
#define UNIT_HEIGHT 12
69
#define MAX_KEY_OPTIONS 4
72
enum de_state_t { STATE_INIT, STATE_RELOAD, STATE_CONTINUE };
73
enum de_player_t { PLAYER_LEFT = 0, PLAYER_RIGHT = 1, MAX_PLAYERS = 2 };
74
enum de_team_t { TEAM_LEFT = 0, TEAM_RIGHT = 1, MAX_TEAMS = 2 };
75
enum de_mode_t { MODE_5CARDWAR = 0, MODE_TRADITIONAL, MODE_HELIASSAULT,
76
MODE_HELIDEFENSE, MODE_OUTGUNNED, MODE_CUSTOM,
77
MODE_FIRST = MODE_5CARDWAR, MODE_LAST = MODE_CUSTOM,
78
MAX_MODES = 6, MODE_NONE = -1 };
79
enum de_unit_t { UNIT_TANK = 0, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE,
80
UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI,
81
UNIT_FIRST = UNIT_TANK, UNIT_LAST = UNIT_HELI,
82
MAX_UNITS = 8, UNIT_NONE = -1 };
83
enum de_shot_t { SHOT_TRACER = 0, SHOT_SMALL, SHOT_LARGE, SHOT_MICRO,
84
SHOT_SUPER, SHOT_DEMO, SHOT_SMALLNUKE, SHOT_LARGENUKE,
85
SHOT_SMALLDIRT, SHOT_LARGEDIRT, SHOT_MAGNET, SHOT_MINILASER,
86
SHOT_MEGALASER, SHOT_LASERTRACER, SHOT_MEGABLAST, SHOT_MINI,
88
SHOT_FIRST = SHOT_TRACER, SHOT_LAST = SHOT_BOMB,
89
MAX_SHOT_TYPES = 17, SHOT_INVALID = -1 };
90
enum de_expl_t { EXPL_NONE, EXPL_MAGNET, EXPL_DIRT, EXPL_NORMAL }; /* this needs a better name */
91
enum de_trails_t { TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL };
92
enum de_pixel_t { PIXEL_BLACK = 0, PIXEL_DIRT = 25 };
93
enum de_mapflags_t { MAP_NORMAL = 0x00, MAP_WALLS = 0x01, MAP_RINGS = 0x02,
94
MAP_HOLES = 0x04, MAP_FUZZY = 0x08, MAP_TALL = 0x10 };
96
/* keys and moves should line up. */
97
enum de_keys_t { KEY_LEFT = 0, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_CHANGE, KEY_FIRE, KEY_CYUP, KEY_CYDN, MAX_KEY = 8};
98
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
/* The tracerlaser is dummied out. It works but (probably due to the low
101
* MAX_SHOTS) is not assigned to anything. The bomb does not work.
106
struct destruct_config_s {
108
unsigned int max_shots;
109
unsigned int min_walls;
110
unsigned int max_walls;
111
unsigned int max_explosions;
112
unsigned int max_installations;
115
bool jumper_straight[2];
118
struct destruct_unit_s {
120
/* Positioning/movement */
121
unsigned int unitX; /* yep, one's an int and the other is a real */
126
/* What it is and what it fires */
127
enum de_unit_t unitType;
128
enum de_shot_t shotType;
130
/* What it's pointed */
136
unsigned int ani_frame;
139
struct destruct_shot_s {
148
unsigned int shottype;
149
//int shotdur; /* This looks to be unused */
150
unsigned int trailx[4], traily[4], trailc[4];
152
struct destruct_explo_s {
157
unsigned int explowidth;
158
unsigned int explomax;
159
unsigned int explofill;
160
enum de_expl_t exploType;
162
struct destruct_moves_s {
163
bool actions[MAX_MOVE];
165
struct destruct_keys_s {
166
SDLKey Config[MAX_KEY][MAX_KEY_OPTIONS];
168
struct destruct_ai_s {
170
int c_Angle, c_Power, c_Fire;
171
unsigned int c_noDown;
173
struct destruct_player_s {
176
struct destruct_ai_s aiMemory;
178
struct destruct_unit_s * unit;
179
struct destruct_moves_s moves;
180
struct destruct_keys_s keys;
183
unsigned int unitsRemaining;
184
unsigned int unitSelected;
185
unsigned int shotDelay;
188
struct destruct_wall_s {
191
unsigned int wallX, wallY;
193
struct destruct_world_s {
195
/* Map data & screen pointer */
196
unsigned int baseMap[320];
197
SDL_Surface * VGAScreen;
198
struct destruct_wall_s * mapWalls;
200
/* Map configuration */
201
enum de_mode_t destructMode;
202
unsigned int mapFlags;
205
/*** Function decs ***/
207
static void JE_destructMain( void );
208
static void JE_introScreen( void );
209
static enum de_mode_t JE_modeSelect( void );
210
static void JE_helpScreen( void );
211
static void JE_pauseScreen( void );
213
//level generating functions
214
static void JE_generateTerrain( void );
215
static void DE_generateBaseTerrain( unsigned int, unsigned int *);
216
static void DE_drawBaseTerrain( unsigned int * );
217
static void DE_generateUnits( unsigned int * );
218
static void DE_generateWalls( struct destruct_world_s * );
219
static void DE_generateRings(SDL_Surface *, Uint8 );
220
static void DE_ResetLevel( void );
221
static unsigned int JE_placementPosition( unsigned int, unsigned int, unsigned int * );
224
static void JE_aliasDirt( SDL_Surface * );
225
static void DE_RunTickDrawCrosshairs( void );
226
static void DE_RunTickDrawHUD( void );
227
static void DE_GravityDrawUnit( enum de_player_t, struct destruct_unit_s * );
228
static void DE_RunTickAnimate( void );
229
static void DE_RunTickDrawWalls( void );
230
static void DE_DrawTrails( struct destruct_shot_s *, unsigned int, unsigned int, unsigned int );
231
static void JE_tempScreenChecking( void );
232
static void JE_superPixel( unsigned int, unsigned int );
233
static void JE_pixCool( unsigned int, unsigned int, Uint8 );
236
static void DE_RunTickGetInput( void );
237
static void DE_ProcessInput( void );
238
static void DE_ResetPlayers( void );
239
static void DE_ResetAI( void );
240
static void DE_ResetActions( void );
241
static void DE_RunTickAI( void );
244
static void DE_RaiseAngle( struct destruct_unit_s * );
245
static void DE_LowerAngle( struct destruct_unit_s * );
246
static void DE_RaisePower( struct destruct_unit_s * );
247
static void DE_LowerPower( struct destruct_unit_s * );
248
static void DE_CycleWeaponUp( struct destruct_unit_s * );
249
static void DE_CycleWeaponDown( struct destruct_unit_s * );
250
static void DE_RunMagnet( enum de_player_t, struct destruct_unit_s * );
251
static void DE_GravityFlyUnit( struct destruct_unit_s * );
252
static void DE_GravityLowerUnit( struct destruct_unit_s * );
253
static void DE_DestroyUnit( enum de_player_t, struct destruct_unit_s * );
254
static void DE_ResetUnits( void );
255
static inline bool DE_isValidUnit( struct destruct_unit_s *);
258
static void DE_ResetWeapons( void );
259
static void DE_RunTickShots( void );
260
static void DE_RunTickExplosions( void );
261
static void DE_TestExplosionCollision( unsigned int, unsigned int);
262
static void JE_makeExplosion( unsigned int, unsigned int, enum de_shot_t );
263
static void DE_MakeShot( enum de_player_t, const struct destruct_unit_s *, int );
266
static enum de_state_t DE_RunTick( void );
267
static void DE_RunTickCycleDeadUnits( void );
268
static void DE_RunTickGravity( void );
269
static bool DE_RunTickCheckEndgame( void );
270
static bool JE_stabilityCheck( unsigned int, unsigned int );
273
static void DE_RunTickPlaySounds( void );
274
static void JE_eSound( unsigned int );
278
/*** Weapon configurations ***/
280
/* Part of me wants to leave these as bytes to save space. */
281
static const bool demolish[MAX_SHOT_TYPES] = {false, false, false, false, false, true, true, true, false, false, false, false, true, false, true, false, true};
282
//static const int shotGr[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101};
283
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};
284
//static const int shotFuse[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0};
285
static const int shotDelay[MAX_SHOT_TYPES] = {10, 30, 80, 20, 60, 100, 140, 200, 20, 60, 5, 15, 50, 5, 80, 16, 0};
286
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};
287
static const int exploSize[MAX_SHOT_TYPES] = {4, 20, 30, 14, 22, 16, 40, 60, 10, 30, 0, 5, 10, 3, 15, 7, 0};
288
static const bool shotBounce[MAX_SHOT_TYPES] = {false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true};
289
static const int exploDensity[MAX_SHOT_TYPES] = { 2, 5, 10, 15, 20, 15, 25, 30, 40, 80, 0, 30, 30, 4, 30, 5, 0};
290
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};
291
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
static const int defaultWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO, SHOT_SMALLDIRT, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI};
294
static const int defaultCpuWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO, SHOT_DEMO, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI};
295
static const int defaultCpuWeaponB[MAX_UNITS] = {SHOT_DEMO, SHOT_SMALLNUKE, SHOT_DEMO, SHOT_INVALID, SHOT_MAGNET, SHOT_MEGALASER, SHOT_MICRO, SHOT_MINI};
296
static const int systemAngle[MAX_UNITS] = {true, true, true, false, false, true, false, false};
297
static const int baseDamage[MAX_UNITS] = {200, 120, 400, 300, 80, 150, 600, 40};
298
static const int systemAni[MAX_UNITS] = {false, false, false, true, false, false, false, true};
300
static bool weaponSystems[MAX_UNITS][MAX_SHOT_TYPES] =
302
{1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // normal
303
{0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // nuke
304
{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, // dirt
305
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // worthless
306
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // magnet
307
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, // laser
308
{1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, // jumper
309
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0} // helicopter
312
/* More constant configuration settings. */
313
/* Music that destruct will play. You can check out musmast.c to see what is what. */
314
static const JE_byte goodsel[14] /*[1..14]*/ = {1, 2, 6, 12, 13, 14, 17, 23, 24, 26, 28, 29, 32, 33};
316
/* Unit creation. Need to move this later: Doesn't belong here */
317
static JE_byte basetypes[10][11] /*[1..8, 1..11]*/ = /* [0] is amount of units*/
319
{5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Normal*/
320
{1, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK}, /*Traditional*/
321
{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*/
322
{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*/
323
{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*/
324
{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*/
325
{8, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_TANK, UNIT_NUKE}, /*Overpowering fleet*/
326
{4, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_TANK, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_NUKE, UNIT_JUMPER}, /*Weak fleet*/
327
{1, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK}, /*Custom1, to be edited*/
328
{1, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK} /*Custom2, to be edited*/
330
static const unsigned int baseLookup[MAX_PLAYERS][MAX_MODES] =
337
static const JE_byte GraphicBase[MAX_PLAYERS][MAX_UNITS] =
339
{ 1, 6, 11, 58, 63, 68, 96, 153},
340
{ 20, 25, 30, 77, 82, 87, 115, 172}
343
static const JE_byte ModeScore[MAX_PLAYERS][MAX_MODES] =
349
static SDLKey defaultKeyConfig[MAX_PLAYERS][MAX_KEY][MAX_KEY_OPTIONS] =
356
{SDLK_x, SDLK_LSHIFT},
360
{ {SDLK_LEFT, SDLK_KP4},
361
{SDLK_RIGHT, SDLK_KP6},
363
{SDLK_DOWN, SDLK_KP2},
364
{SDLK_BACKSLASH, SDLK_KP5},
365
{SDLK_INSERT, SDLK_RETURN, SDLK_KP0, SDLK_KP_ENTER},
366
{SDLK_PAGEUP, SDLK_KP9},
367
{SDLK_PAGEDOWN, SDLK_KP3}
373
static SDL_Surface *destructTempScreen;
374
static JE_boolean destructFirstTime;
376
static struct destruct_config_s config = { 40, 20, 20, 40, 10, false, false, {true, false}, {true, false} };
377
static struct destruct_player_s destruct_player[MAX_PLAYERS];
378
static struct destruct_world_s world;
379
static struct destruct_shot_s * shotRec;
380
static struct destruct_explo_s * exploRec;
384
static enum de_unit_t string_to_unit_enum(const char * str) {
386
// A config helper function. Probably not useful anywhere else.
388
static const char * unit_names[] =
389
{ "UNIT_TANK", "UNIT_NUKE", "UNIT_DIRT", "UNIT_SATELLITE",
390
"UNIT_MAGNET", "UNIT_LASER", "UNIT_JUMPER", "UNIT_HELI" };
392
for (i = UNIT_FIRST; i < MAX_UNITS; i++) {
393
if(strcmp(unit_names[i], str) == 0) { return(i); }
398
static bool write_default_destruct_config( void ) {
401
cJSON * level1, * level2, * level3, * setting;
404
//If I read the file right, all of these will return NULL on failure.
405
//Well that'll be a little bit tedious to check for each time, but using
406
//gotos can help clear everything up since only one thing is freed.
407
if((root = cJSON_CreateObject()) == NULL) { goto label_failure; }
410
if((level1 = cJSON_CreateOrGetObjectItem(root, "general")) == NULL) { goto label_failure; }
411
cJSON_ForceType(level1, cJSON_Object);
414
if((setting = cJSON_CreateOrGetObjectItem(level1, "alwaysalias")) == NULL) { goto label_failure; }
415
cJSON_SetBoolean(setting, false);
416
if((setting = cJSON_CreateOrGetObjectItem(level1, "tracerlaser")) == NULL) { goto label_failure; }
417
cJSON_SetBoolean(setting, false);
418
if((setting = cJSON_CreateOrGetObjectItem(level1, "max_shots")) == NULL) { goto label_failure; }
419
cJSON_SetNumber(setting, 40);
420
if((setting = cJSON_CreateOrGetObjectItem(level1, "min_walls")) == NULL) { goto label_failure; }
421
cJSON_SetNumber(setting, 20);
422
if((setting = cJSON_CreateOrGetObjectItem(level1, "max_walls")) == NULL) { goto label_failure; }
423
cJSON_SetNumber(setting, 20);
424
if((setting = cJSON_CreateOrGetObjectItem(level1, "max_explosions")) == NULL) { goto label_failure; }
425
cJSON_SetNumber(setting, 40);
428
if((level2 = cJSON_CreateOrGetObjectItem(level1, "player1")) == NULL) { goto label_failure; }
429
cJSON_ForceType(level2, cJSON_Object);
430
if((setting = cJSON_CreateOrGetObjectItem(level2, "ai")) == NULL) { goto label_failure; }
431
cJSON_SetBoolean(setting, true);
432
if((setting = cJSON_CreateOrGetObjectItem(level2, "jumper_fires_straight")) == NULL) { goto label_failure; }
433
cJSON_SetBoolean(setting, true);
435
if((level3 = cJSON_CreateOrGetObjectItem(level2, "keys")) == NULL) { goto label_failure; }
436
cJSON_ForceType(level3, cJSON_Object);
437
if((setting = cJSON_CreateOrGetObjectItem(level3, "__comment")) == NULL) { goto label_failure; }
438
cJSON_SetString(setting, "You may configure the keys here. Nums correspond to SDL defines. It's better than nothing.");
439
if((setting = cJSON_CreateOrGetObjectItem(level3, "left1")) == NULL) { goto label_failure; }
440
cJSON_SetNumber(setting, SDLK_c);
441
if((setting = cJSON_CreateOrGetObjectItem(level3, "right1")) == NULL) { goto label_failure; }
442
cJSON_SetNumber(setting, SDLK_v);
443
if((setting = cJSON_CreateOrGetObjectItem(level3, "up1")) == NULL) { goto label_failure; }
444
cJSON_SetNumber(setting, SDLK_a);
445
if((setting = cJSON_CreateOrGetObjectItem(level3, "down1")) == NULL) { goto label_failure; }
446
cJSON_SetNumber(setting, SDLK_z);
447
if((setting = cJSON_CreateOrGetObjectItem(level3, "change1")) == NULL) { goto label_failure; }
448
cJSON_SetNumber(setting, SDLK_LALT);
449
if((setting = cJSON_CreateOrGetObjectItem(level3, "fire1")) == NULL) { goto label_failure; }
450
cJSON_SetNumber(setting, SDLK_x);
451
if((setting = cJSON_CreateOrGetObjectItem(level3, "fire2")) == NULL) { goto label_failure; }
452
cJSON_SetNumber(setting, SDLK_LSHIFT);
453
if((setting = cJSON_CreateOrGetObjectItem(level3, "cyup1")) == NULL) { goto label_failure; }
454
cJSON_SetNumber(setting, SDLK_LCTRL);
455
if((setting = cJSON_CreateOrGetObjectItem(level3, "cydn1")) == NULL) { goto label_failure; }
456
cJSON_SetNumber(setting, SDLK_SPACE);
458
if((level2 = cJSON_CreateOrGetObjectItem(level1, "player2")) == NULL) { goto label_failure; }
459
cJSON_ForceType(level2, cJSON_Object);
460
if((setting = cJSON_CreateOrGetObjectItem(level2, "ai")) == NULL) { goto label_failure; }
461
cJSON_SetBoolean(setting, false);
462
if((setting = cJSON_CreateOrGetObjectItem(level2, "jumper_fires_straight")) == NULL) { goto label_failure; }
463
cJSON_SetBoolean(setting, false);
465
if((level3 = cJSON_CreateOrGetObjectItem(level2, "keys")) == NULL) { goto label_failure; }
466
cJSON_ForceType(level3, cJSON_Object);
467
if((setting = cJSON_CreateOrGetObjectItem(level3, "left1")) == NULL) { goto label_failure; }
468
cJSON_SetNumber(setting, SDLK_LEFT);
469
if((setting = cJSON_CreateOrGetObjectItem(level3, "left2")) == NULL) { goto label_failure; }
470
cJSON_SetNumber(setting, SDLK_KP4);
471
if((setting = cJSON_CreateOrGetObjectItem(level3, "right1")) == NULL) { goto label_failure; }
472
cJSON_SetNumber(setting, SDLK_RIGHT);
473
if((setting = cJSON_CreateOrGetObjectItem(level3, "right2")) == NULL) { goto label_failure; }
474
cJSON_SetNumber(setting, SDLK_KP6);
475
if((setting = cJSON_CreateOrGetObjectItem(level3, "up1")) == NULL) { goto label_failure; }
476
cJSON_SetNumber(setting, SDLK_UP);
477
if((setting = cJSON_CreateOrGetObjectItem(level3, "up2")) == NULL) { goto label_failure; }
478
cJSON_SetNumber(setting, SDLK_KP8);
479
if((setting = cJSON_CreateOrGetObjectItem(level3, "down1")) == NULL) { goto label_failure; }
480
cJSON_SetNumber(setting, SDLK_DOWN);
481
if((setting = cJSON_CreateOrGetObjectItem(level3, "down2")) == NULL) { goto label_failure; }
482
cJSON_SetNumber(setting, SDLK_KP2);
483
if((setting = cJSON_CreateOrGetObjectItem(level3, "change1")) == NULL) { goto label_failure; }
484
cJSON_SetNumber(setting, SDLK_BACKSLASH);
485
if((setting = cJSON_CreateOrGetObjectItem(level3, "change2")) == NULL) { goto label_failure; }
486
cJSON_SetNumber(setting, SDLK_KP5);
487
if((setting = cJSON_CreateOrGetObjectItem(level3, "fire1")) == NULL) { goto label_failure; }
488
cJSON_SetNumber(setting, SDLK_INSERT);
489
if((setting = cJSON_CreateOrGetObjectItem(level3, "fire2")) == NULL) { goto label_failure; }
490
cJSON_SetNumber(setting, SDLK_RETURN);
491
if((setting = cJSON_CreateOrGetObjectItem(level3, "fire3")) == NULL) { goto label_failure; }
492
cJSON_SetNumber(setting, SDLK_KP0);
493
if((setting = cJSON_CreateOrGetObjectItem(level3, "fire4")) == NULL) { goto label_failure; }
494
cJSON_SetNumber(setting, SDLK_KP_ENTER);
495
if((setting = cJSON_CreateOrGetObjectItem(level3, "cyup1")) == NULL) { goto label_failure; }
496
cJSON_SetNumber(setting, SDLK_PAGEUP);
497
if((setting = cJSON_CreateOrGetObjectItem(level3, "cyup2")) == NULL) { goto label_failure; }
498
cJSON_SetNumber(setting, SDLK_KP9);
499
if((setting = cJSON_CreateOrGetObjectItem(level3, "cydn1")) == NULL) { goto label_failure; }
500
cJSON_SetNumber(setting, SDLK_PAGEDOWN);
501
if((setting = cJSON_CreateOrGetObjectItem(level3, "cydn2")) == NULL) { goto label_failure; }
502
cJSON_SetNumber(setting, SDLK_KP3);
505
if((level1 = cJSON_CreateOrGetObjectItem(root, "custom")) == NULL) { goto label_failure; }
506
cJSON_ForceType(level1, cJSON_Object);
508
if((setting = cJSON_CreateOrGetObjectItem(level1, "enable")) == NULL) { goto label_failure; }
509
cJSON_SetBoolean(setting, false);
511
//player 1 (I could but won't bother looping this)
512
if((level2 = cJSON_CreateOrGetObjectItem(level1, "player1")) == NULL) { goto label_failure; }
513
cJSON_ForceType(level2, cJSON_Object);
514
if((setting = cJSON_CreateOrGetObjectItem(level2, "num_units")) == NULL) { goto label_failure; }
515
cJSON_SetNumber(setting, 10);
516
if((setting = cJSON_CreateOrGetObjectItem(level2, "__comment")) == NULL) { goto label_failure; }
517
cJSON_SetString(setting, "This handles probability. Always have 10 entries.");
519
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit1")) == NULL) { goto label_failure; }
520
cJSON_SetString(setting, "UNIT_TANK");
521
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit2")) == NULL) { goto label_failure; }
522
cJSON_SetString(setting, "UNIT_TANK");
523
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit3")) == NULL) { goto label_failure; }
524
cJSON_SetString(setting, "UNIT_NUKE");
525
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit4")) == NULL) { goto label_failure; }
526
cJSON_SetString(setting, "UNIT_DIRT");
527
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit5")) == NULL) { goto label_failure; }
528
cJSON_SetString(setting, "UNIT_DIRT");
529
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit6")) == NULL) { goto label_failure; }
530
cJSON_SetString(setting, "UNIT_SATELLITE");
531
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit7")) == NULL) { goto label_failure; }
532
cJSON_SetString(setting, "UNIT_MAGNET");
533
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit8")) == NULL) { goto label_failure; }
534
cJSON_SetString(setting, "UNIT_LASER");
535
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit9")) == NULL) { goto label_failure; }
536
cJSON_SetString(setting, "UNIT_JUMPER");
537
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit10")) == NULL) { goto label_failure; }
538
cJSON_SetString(setting, "UNIT_HELI");
540
if((level2 = cJSON_CreateOrGetObjectItem(level1, "player2")) == NULL) { goto label_failure; }
541
cJSON_ForceType(level2, cJSON_Object);
542
if((setting = cJSON_CreateOrGetObjectItem(level2, "num_units")) == NULL) { goto label_failure; }
543
cJSON_SetNumber(setting, 10);
545
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit1")) == NULL) { goto label_failure; }
546
cJSON_SetString(setting, "UNIT_TANK");
547
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit2")) == NULL) { goto label_failure; }
548
cJSON_SetString(setting, "UNIT_TANK");
549
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit3")) == NULL) { goto label_failure; }
550
cJSON_SetString(setting, "UNIT_NUKE");
551
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit4")) == NULL) { goto label_failure; }
552
cJSON_SetString(setting, "UNIT_DIRT");
553
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit5")) == NULL) { goto label_failure; }
554
cJSON_SetString(setting, "UNIT_DIRT");
555
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit6")) == NULL) { goto label_failure; }
556
cJSON_SetString(setting, "UNIT_SATELLITE");
557
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit7")) == NULL) { goto label_failure; }
558
cJSON_SetString(setting, "UNIT_MAGNET");
559
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit8")) == NULL) { goto label_failure; }
560
cJSON_SetString(setting, "UNIT_LASER");
561
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit9")) == NULL) { goto label_failure; }
562
cJSON_SetString(setting, "UNIT_JUMPER");
563
if((setting = cJSON_CreateOrGetObjectItem(level2, "unit10")) == NULL) { goto label_failure; }
564
cJSON_SetString(setting, "UNIT_HELI");
566
save_json(root, "destruct.conf");
573
static void load_destruct_config( void ) {
579
const char * key_names[] = { "left", "right", "up", "down", "change", "fire", "cyup", "cydn" };
581
cJSON * level1, * level2, * level3, * setting;
583
// The config file is not modified in game in order to 'keep' with the
584
// original (unconfigurable) feel. This code was copied from elsewhere.
585
root = load_json("destruct.conf");
587
write_default_destruct_config();
591
//load these general config items. I don't consider sanity checks
592
//necessary; either the game isn't playable or you eat up all your memory
593
//when using unreasonable values. Either way, no exploit here.
594
level1 = cJSON_GetObjectItem(root, "general");
597
if ((setting = cJSON_GetObjectItem(level1, "alwaysalias"))) {
598
config.alwaysalias = (setting->type == cJSON_True);
600
if ((setting = cJSON_GetObjectItem(level1, "tracerlaser"))) {
601
weaponSystems[UNIT_LASER][SHOT_LASERTRACER] = (setting->type == cJSON_True);
603
if ((setting = cJSON_GetObjectItem(level1, "max_shots")) && setting->type == cJSON_Number) {
604
config.max_shots = setting->valueint;
606
if ((setting = cJSON_GetObjectItem(level1, "min_walls")) && setting->type == cJSON_Number) {
607
config.min_walls = setting->valueint;
609
if ((setting = cJSON_GetObjectItem(level1, "max_walls")) && setting->type == cJSON_Number) {
610
config.max_walls = setting->valueint;
611
if(config.min_walls > config.max_walls) { config.min_walls = config.max_walls; }
613
if ((setting = cJSON_GetObjectItem(level1, "max_explosions")) && setting->type == cJSON_Number) {
614
config.max_explosions = setting->valueint;
617
//player configuration
618
for(i = PLAYER_LEFT; i < MAX_PLAYERS; i++) {
619
sprintf(buffer, "player%i", i+1);
620
level2 = cJSON_GetObjectItem(level1, buffer);
623
if ((setting = cJSON_GetObjectItem(level2, "jumper_fires_straight"))) {
624
config.jumper_straight[i] = (setting->type == cJSON_True);
626
if ((setting = cJSON_GetObjectItem(level2, "ai"))) {
627
config.ai[i] = (setting->type == cJSON_True);
630
level3 = cJSON_GetObjectItem(level2, "keys");
633
for (j = 0; j < COUNTOF(key_names); j++) {
634
for (k = 0; k < MAX_KEY_OPTIONS; k++) {
635
sprintf(buffer, "%s%i", key_names[j], k+1);
636
if ((setting = cJSON_GetObjectItem(level3, buffer)) && setting->type == cJSON_Number) {
637
defaultKeyConfig[i][j][k] = setting->valueint;
639
else { //assume that if we are reading keys the defaults are null and void
640
defaultKeyConfig[i][j][k] = SDLK_UNKNOWN;
649
//Now let's hit the custom mode...
650
level1 = cJSON_GetObjectItem(root, "custom");
655
if ((setting = cJSON_GetObjectItem(level1, "enable"))) {
656
config.allow_custom = (setting->type == cJSON_True);
659
//player configuration
660
for(i = PLAYER_LEFT; i < MAX_PLAYERS; i++) {
661
sprintf(buffer, "player%i", i+1);
662
level2 = cJSON_GetObjectItem(level1, buffer);
665
if ((setting = cJSON_GetObjectItem(level2, "num_units"))) {
666
basetypes[8 + i][0] = setting->valueint;
668
for(j = 1; j < 11; j++) {
669
sprintf(buffer, "unit%i", j);
670
if ((setting = cJSON_GetObjectItem(level2, buffer)) && setting->type == cJSON_String) {
671
temp = string_to_unit_enum(setting->valuestring);
672
if(temp != UNIT_NONE) {
673
basetypes[8 + i][j] = temp;
684
void JE_destructGame( void )
688
/* This is the entry function. Any one-time actions we need to
689
* perform can go in here. */
690
JE_clr256(VGAScreen);
693
load_destruct_config();
695
//malloc things that have customizable sizes
696
shotRec = malloc(sizeof(struct destruct_shot_s) * config.max_shots);
697
exploRec = malloc(sizeof(struct destruct_explo_s) * config.max_explosions);
698
world.mapWalls = malloc(sizeof(struct destruct_wall_s) * config.max_walls);
700
//Malloc enough structures to cover all of this session's possible needs.
701
for(i = 0; i < 10; i++) {
702
config.max_installations = MAX(config.max_installations, basetypes[i][0]);
704
destruct_player[PLAYER_LEFT ].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations);
705
destruct_player[PLAYER_RIGHT].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations);
707
destructTempScreen = game_screen;
708
world.VGAScreen = VGAScreen;
710
JE_loadCompShapes(&eShapes[0], '~');
715
//and of course exit actions go here.
718
free(world.mapWalls);
719
free(destruct_player[PLAYER_LEFT ].unit);
720
free(destruct_player[PLAYER_RIGHT].unit);
723
static void JE_destructMain( void )
725
enum de_state_t curState;
728
JE_loadPic(VGAScreen, 11, false);
733
destruct_player[PLAYER_LEFT ].is_cpu = config.ai[PLAYER_LEFT];
734
destruct_player[PLAYER_RIGHT].is_cpu = config.ai[PLAYER_RIGHT];
738
world.destructMode = JE_modeSelect();
740
if(world.destructMode == MODE_NONE) {
741
break; /* User is quitting */
747
destructFirstTime = true;
748
JE_loadPic(VGAScreen, 11, false);
753
curState = DE_RunTick();
754
} while(curState == STATE_CONTINUE);
758
while (curState == STATE_RELOAD);
762
static void JE_introScreen( void )
764
memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
765
JE_outText(VGAScreen, JE_fontCenter(specialName[7], TINY_FONT), 90, specialName[7], 12, 5);
766
JE_outText(VGAScreen, JE_fontCenter(miscText[64], TINY_FONT), 180, miscText[64], 15, 2);
767
JE_outText(VGAScreen, JE_fontCenter(miscText[65], TINY_FONT), 190, miscText[65], 15, 2);
769
fade_palette(colors, 15, 0, 255);
774
service_SDL_events(false);
779
memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
785
* This function prints the DESTRUCT mode selection menu.
786
* The return value is the selected mode, or -1 (MODE_NONE)
789
static void DrawModeSelectMenu( enum de_mode_t mode ) {
793
/* Helper function of JE_modeSelect. Do not use elsewhere. */
794
for (i = 0; i < DESTRUCT_MODES; i++)
795
{ /* What a large function call. */
796
JE_textShade(VGAScreen, JE_fontCenter(destructModeName[i], TINY_FONT), 82 + i * 12, destructModeName[i], 12, (i == mode) * 4, FULL_SHADE);
798
if (config.allow_custom == true)
800
JE_textShade(VGAScreen, JE_fontCenter("Custom", TINY_FONT), 82 + i * 12, "Custom", 12, (i == mode) * 4, FULL_SHADE);
803
static enum de_mode_t JE_modeSelect( void )
808
memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
809
mode = MODE_5CARDWAR;
811
// Draw the menu and fade us in
812
DrawModeSelectMenu(mode);
815
fade_palette(colors, 15, 0, 255);
817
/* Get input in a loop. */
820
/* Re-draw the menu every iteration */
821
DrawModeSelectMenu(mode);
827
service_SDL_events(false);
831
/* See what was pressed */
832
if (keysactive[SDLK_ESCAPE])
834
mode = MODE_NONE; /* User is quitting, return failure */
837
if (keysactive[SDLK_RETURN])
839
break; /* User has selected, return choice */
841
if (keysactive[SDLK_UP])
843
if(mode == MODE_FIRST)
845
if (config.allow_custom == true)
855
if (keysactive[SDLK_DOWN])
857
if(mode >= MODE_LAST-1)
859
if (config.allow_custom == true && mode == MODE_LAST-1)
872
memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
877
static void JE_generateTerrain( void )
879
/* The unique modifiers:
880
Altered generation (really tall)
884
The non-unique ones;:
885
Rings of not dirt (holes)
889
world.mapFlags = MAP_NORMAL;
891
if(mt_rand() % 2 == 0)
893
world.mapFlags |= MAP_WALLS;
895
if(mt_rand() % 4 == 0)
897
world.mapFlags |= MAP_HOLES;
899
switch(mt_rand() % 4)
902
world.mapFlags |= MAP_FUZZY;
906
world.mapFlags |= MAP_TALL;
910
world.mapFlags |= MAP_RINGS;
914
play_song(goodsel[mt_rand() % 14] - 1);
916
DE_generateBaseTerrain(world.mapFlags, world.baseMap);
917
DE_generateUnits(world.baseMap);
918
DE_generateWalls(&world);
919
DE_drawBaseTerrain(world.baseMap);
921
if (world.mapFlags & MAP_RINGS)
923
DE_generateRings(world.VGAScreen, PIXEL_DIRT);
925
if (world.mapFlags & MAP_HOLES)
927
DE_generateRings(world.VGAScreen, PIXEL_BLACK);
930
JE_aliasDirt(world.VGAScreen);
933
memcpy(destructTempScreen->pixels, VGAScreen->pixels, destructTempScreen->pitch * destructTempScreen->h);
935
static void DE_generateBaseTerrain( unsigned int mapFlags, unsigned int * baseWorld)
938
unsigned int newheight, HeightMul;
939
float sinewave, sinewave2, cosinewave, cosinewave2;
942
/* The 'terrain' is actually the video buffer :). If it's brown, flu... er,
943
* brown pixels are what we check for collisions with. */
945
/* The ranges here are between .01 and roughly 0.07283...*/
946
sinewave = mt_rand_lt1() * M_PI / 50 + 0.01f;
947
sinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f;
948
cosinewave = mt_rand_lt1() * M_PI / 50 + 0.01f;
949
cosinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f;
952
/* This block just exists to mix things up. */
953
if(mapFlags & MAP_FUZZY)
955
sinewave = M_PI - mt_rand_lt1() * 0.3f;
956
sinewave2 = M_PI - mt_rand_lt1() * 0.3f;
958
if(mapFlags & MAP_TALL)
963
/* Now compute a height for each of our lines. */
964
for (i = 1; i <= 318; i++)
966
newheight = roundf(sinf(sinewave * i) * HeightMul + sinf(sinewave2 * i) * 15 +
967
cosf(cosinewave * i) * 10 + sinf(cosinewave2 * i) * 15) + 130;
969
/* Bind it; we have mins and maxs */
974
else if (newheight > 195) {
977
baseWorld[i] = newheight;
979
/* The base world has been created. */
981
static void DE_drawBaseTerrain( unsigned int * baseWorld)
986
for (i = 1; i <= 318; i++)
988
JE_rectangle(VGAScreen, i, baseWorld[i], i, 199, PIXEL_DIRT);
992
static void DE_generateUnits( unsigned int * baseWorld )
994
unsigned int i, j, numSatellites;
997
for (i = 0; i < MAX_PLAYERS; i++)
1000
destruct_player[i].unitsRemaining = 0;
1002
for (j = 0; j < basetypes[baseLookup[i][world.destructMode]][0]; j++)
1004
/* Not everything is the same between players */
1005
if(i == PLAYER_LEFT)
1007
destruct_player[i].unit[j].unitX = (mt_rand() % 120) + 10;
1011
destruct_player[i].unit[j].unitX = 320 - ((mt_rand() % 120) + 22);
1014
destruct_player[i].unit[j].unitY = JE_placementPosition(destruct_player[i].unit[j].unitX - 1, 14, baseWorld);
1015
destruct_player[i].unit[j].unitType = basetypes[baseLookup[i][world.destructMode]][(mt_rand() % 10) + 1];
1017
/* Sats are special cases since they are useless. They don't count
1018
* as active units and we can't have a team of all sats */
1019
if (destruct_player[i].unit[j].unitType == UNIT_SATELLITE)
1021
if (numSatellites == basetypes[baseLookup[i][world.destructMode]][0])
1023
destruct_player[i].unit[j].unitType = UNIT_TANK;
1024
destruct_player[i].unitsRemaining++;
1026
/* Place the satellite. Note: Earlier we cleared
1027
* space with JE_placementPosition. Now we are randomly
1028
* placing the sat's Y. It can be generated in hills
1029
* and there is a clearing underneath it. This CAN
1030
* be fixed but won't be for classic.
1032
destruct_player[i].unit[j].unitY = 30 + (mt_rand() % 40);
1038
destruct_player[i].unitsRemaining++;
1041
/* Now just fill in the rest of the unit's values. */
1042
destruct_player[i].unit[j].lastMove = 0;
1043
destruct_player[i].unit[j].unitYMov = 0;
1044
destruct_player[i].unit[j].isYInAir = false;
1045
destruct_player[i].unit[j].angle = 0;
1046
destruct_player[i].unit[j].power = (destruct_player[i].unit[j].unitType == UNIT_LASER) ? 6 : 3;
1047
destruct_player[i].unit[j].shotType = defaultWeapon[destruct_player[i].unit[j].unitType];
1048
destruct_player[i].unit[j].health = baseDamage[destruct_player[i].unit[j].unitType];
1049
destruct_player[i].unit[j].ani_frame = 0;
1053
static void DE_generateWalls( struct destruct_world_s * gameWorld )
1055
unsigned int i, j, wallX;
1056
unsigned int wallHeight, remainWalls;
1061
if ((world.mapFlags & MAP_WALLS) == false)
1063
/* Just clear them out */
1064
for (i = 0; i < config.max_walls; i++)
1066
gameWorld->mapWalls[i].wallExist = false;
1071
remainWalls = (rand() % (config.max_walls - config.min_walls + 1)) + config.min_walls;
1075
/* Create a wall. Decide how tall the wall will be */
1076
wallHeight = (mt_rand() % 5) + 1;
1077
if(wallHeight > remainWalls)
1079
wallHeight = remainWalls;
1082
/* Now find a good place to put the wall. */
1087
wallX = (mt_rand() % 300) + 10;
1089
/* Is this X already occupied? In the original Tyrian we only
1090
* checked to make sure four units on each side were unobscured.
1091
* That's not very scalable; instead I will check every unit,
1092
* but I'll only try plotting an unobstructed X four times.
1093
* After that we'll cover up what may; having a few units
1094
* stuck behind walls makes things mildly interesting.
1096
for (i = 0; i < MAX_PLAYERS; i++)
1098
for (j = 0; j < config.max_installations; j++)
1100
if ((wallX > destruct_player[i].unit[j].unitX - 12)
1101
&& (wallX < destruct_player[i].unit[j].unitX + 13))
1104
goto label_outer_break; /* I do feel that outer breaking is a legitimate goto use. */
1112
} while(isGood == false && tries < 5);
1115
/* We now have a valid X. Create the wall. */
1116
for (i = 1; i <= wallHeight; i++)
1118
gameWorld->mapWalls[remainWalls - i].wallExist = true;
1119
gameWorld->mapWalls[remainWalls - i].wallX = wallX;
1120
gameWorld->mapWalls[remainWalls - i].wallY = JE_placementPosition(wallX, 12, gameWorld->baseMap) - 14 * i;
1123
remainWalls -= wallHeight;
1125
} while (remainWalls != 0);
1128
static void DE_generateRings( SDL_Surface * screen, Uint8 pixel )
1130
unsigned int i, j, tempSize, rings;
1131
int tempPosX1, tempPosY1, tempPosX2, tempPosY2;
1135
rings = mt_rand() % 6 + 1;
1136
for (i = 1; i <= rings; i++)
1138
tempPosX1 = (mt_rand() % 320);
1139
tempPosY1 = (mt_rand() % 160) + 20;
1140
tempSize = (mt_rand() % 40) + 10; /*Size*/
1142
for (j = 1; j <= tempSize * tempSize * 2; j++)
1144
tempRadian = mt_rand_lt1() * (2 * M_PI);
1145
tempPosY2 = tempPosY1 + roundf(cosf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize);
1146
tempPosX2 = tempPosX1 + roundf(sinf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize);
1147
if ((tempPosY2 > 12) && (tempPosY2 < 200)
1148
&& (tempPosX2 > 0) && (tempPosX2 < 319))
1150
((Uint8 *)screen->pixels)[tempPosX2 + tempPosY2 * screen->pitch] = pixel;
1156
static unsigned int aliasDirtPixel(const SDL_Surface * screen, unsigned int x, unsigned int y, const Uint8 * s) {
1158
//A helper function used when aliasing dirt. That's a messy process;
1159
//let's contain the mess here.
1160
unsigned int newColor = PIXEL_BLACK;
1163
if ((y > 0) && (*(s - screen->pitch) == PIXEL_DIRT)) { // look up
1166
if ((y < screen->h - 1u) && (*(s + screen->pitch) == PIXEL_DIRT)) { // look down
1169
if ((x > 0) && (*(s - 1) == PIXEL_DIRT)) { // look left
1172
if ((x < screen->pitch - 1u) && (*(s + 1) == PIXEL_DIRT)) { // look right
1175
if (newColor != PIXEL_BLACK) {
1176
return(newColor + 16); // 16 must be the start of the brown pixels.
1179
return(PIXEL_BLACK);
1181
static void JE_aliasDirt( SDL_Surface * screen )
1183
/* This complicated looking function goes through the whole screen
1184
* looking for brown pixels which just happen to be next to non-brown
1185
* pixels. It's an aliaser, just like it says. */
1189
/* This is a pointer to a screen. If you don't like pointer arithmetic,
1190
* you won't like this function. */
1191
Uint8 *s = screen->pixels;
1192
s += 12 * screen->pitch;
1194
for (y = 12; y < (unsigned)screen->h; y++) {
1195
for (x = 0; x < screen->pitch; x++) {
1196
if (*s == PIXEL_BLACK) {
1197
*s = aliasDirtPixel(screen, x, y, s);
1205
static unsigned int JE_placementPosition( unsigned int passed_x, unsigned int width, unsigned int * world )
1207
unsigned int i, new_y;
1210
/* This is the function responsible for carving out chunks of land.
1211
* There's a bug here, but it's a pretty major gameplay altering one:
1212
* areas can be carved out for units that are aerial or in mountains.
1213
* This can result in huge caverns. Ergo, it's a feature :)
1215
* I wondered if it might be better to not carve out land at all.
1216
* On testing I determined that was distracting and added nothing. */
1218
for (i = passed_x; i <= passed_x + width - 1; i++)
1220
if (new_y < world[i])
1224
for (i = passed_x; i <= passed_x + width - 1; i++)
1232
static bool JE_stabilityCheck( unsigned int x, unsigned int y )
1234
unsigned int i, numDirtPixels;
1239
s = destructTempScreen->pixels;
1240
s += x + (y * destructTempScreen->pitch) - 1;
1242
/* Check the 12 pixels on the bottom border of our object */
1243
for (i = 0; i < 12; i++)
1245
if (*s == PIXEL_DIRT)
1251
/* If there are fewer than 10 brown pixels we don't consider it a solid base */
1252
return (numDirtPixels < 10);
1255
static void JE_tempScreenChecking( void ) /*and copy to vgascreen*/
1257
Uint8 *s = VGAScreen->pixels;
1258
s += 12 * VGAScreen->pitch;
1260
Uint8 *temps = destructTempScreen->pixels;
1261
temps += 12 * destructTempScreen->pitch;
1263
for (int y = 12; y < VGAScreen->h; y++)
1265
for (int x = 0; x < VGAScreen->pitch; x++)
1267
// This block is what fades out explosions. The palette from 241
1268
// to 255 fades from a very dark red to a very bright yellow.
1272
*temps = PIXEL_BLACK;
1277
// This block is for aliasing dirt. Computers are fast these days,
1279
if (config.alwaysalias == true && *temps == PIXEL_BLACK) {
1280
*temps = aliasDirtPixel(VGAScreen, x, y, temps);
1283
/* This is copying from our temp screen to VGAScreen */
1292
static void JE_makeExplosion( unsigned int tempPosX, unsigned int tempPosY, enum de_shot_t shottype )
1294
unsigned int i, tempExploSize;
1297
/* First find an open explosion. If we can't find one, return.*/
1298
for (i = 0; i < config.max_explosions; i++)
1300
if (exploRec[i].isAvailable == true)
1303
if (i == config.max_explosions) /* No empty slots */
1309
exploRec[i].isAvailable = false;
1310
exploRec[i].x = tempPosX;
1311
exploRec[i].y = tempPosY;
1312
exploRec[i].explowidth = 2;
1314
if(shottype != SHOT_INVALID)
1316
tempExploSize = exploSize[shottype];
1317
if (tempExploSize < 5)
1319
else if (tempExploSize < 15)
1321
else if (tempExploSize < 20)
1323
else if (tempExploSize < 40)
1331
exploRec[i].explomax = tempExploSize;
1332
exploRec[i].explofill = exploDensity[shottype];
1333
exploRec[i].exploType = shotDirt[shottype];
1338
exploRec[i].explomax = (mt_rand() % 40) + 10;
1339
exploRec[i].explofill = (mt_rand() % 60) + 20;
1340
exploRec[i].exploType = EXPL_NORMAL;
1344
static void JE_eSound( unsigned int sound )
1346
static int exploSoundChannel = 0;
1348
if (++exploSoundChannel > 5)
1350
exploSoundChannel = 1;
1353
soundQueue[exploSoundChannel] = sound;
1356
static void JE_superPixel( unsigned int tempPosX, unsigned int tempPosY )
1358
const unsigned int starPattern[5][5] = {
1359
{ 0, 0, 246, 0, 0 },
1360
{ 0, 247, 249, 247, 0 },
1361
{ 246, 249, 252, 249, 246 },
1362
{ 0, 247, 249, 247, 0 },
1365
const unsigned int starIntensity[5][5] = {
1373
int x, y, maxX, maxY;
1374
unsigned int rowLen;
1378
maxX = destructTempScreen->pitch;
1379
maxY = destructTempScreen->h;
1381
rowLen = destructTempScreen->pitch;
1382
s = destructTempScreen->pixels;
1383
s += (rowLen * (tempPosY - 2)) + (tempPosX - 2);
1385
for (y = 0; y < 5; y++, s += rowLen - 5)
1387
if ((signed)tempPosY + y - 2 < 0 /* would be out of bounds */
1388
|| (signed)tempPosY + y - 2 >= maxY) { continue; }
1390
for (x = 0; x < 5; x++, s++)
1392
if ((signed)tempPosX + x - 2 < 0
1393
|| (signed)tempPosX + x - 2 >= maxX) { continue; }
1395
if (starPattern[y][x] == 0) { continue; } /* this is just to speed it up */
1397
/* at this point *s is our pixel. Our constant arrays tell us what
1399
if (*s < starPattern[y][x])
1401
*s = starPattern[y][x];
1403
else if (*s + starIntensity[y][x] > 255)
1409
*s += starIntensity[y][x];
1415
static void JE_helpScreen( void )
1420
//JE_getVGA(); didn't do anything anyway?
1422
memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
1423
JE_clr256(VGAScreen);
1425
for(i = 0; i < 2; i++)
1427
JE_outText(VGAScreen, 100, 5 + i * 90, destructHelp[i * 12 + 0], 2, 4);
1428
JE_outText(VGAScreen, 100, 15 + i * 90, destructHelp[i * 12 + 1], 2, 1);
1429
for (j = 3; j <= 12; j++)
1431
JE_outText(VGAScreen, ((j - 1) % 2) * 160 + 10, 15 + ((j - 1) / 2) * 12 + i * 90, destructHelp[i * 12 + j-1], 1, 3);
1434
JE_outText(VGAScreen, 30, 190, destructHelp[24], 3, 4);
1436
fade_palette(colors, 15, 0, 255);
1438
do /* wait until user hits a key */
1440
service_SDL_events(true);
1446
memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
1448
fade_palette(colors, 15, 0, 255);
1452
static void JE_pauseScreen( void )
1454
set_volume(tyrMusicVolume / 2, fxVolume);
1456
/* Save our current screen/game world. We don't want to screw it up while paused. */
1457
memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch);
1458
JE_outText(VGAScreen, JE_fontCenter(miscText[22], TINY_FONT), 90, miscText[22], 12, 5);
1461
do /* wait until user hits a key */
1463
service_SDL_events(true);
1468
/* Restore current screen & volume*/
1469
memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch);
1472
set_volume(tyrMusicVolume, fxVolume);
1477
* The reset functions clear the state of whatefer they are assigned to.
1479
static void DE_ResetUnits( void )
1484
for (p = 0; p < MAX_PLAYERS; ++p)
1485
for (u = 0; u < config.max_installations; ++u)
1486
destruct_player[p].unit[u].health = 0;
1488
static void DE_ResetPlayers( void )
1493
for (i = 0; i < MAX_PLAYERS; ++i)
1495
destruct_player[i].is_cpu = false;
1496
destruct_player[i].unitSelected = 0;
1497
destruct_player[i].shotDelay = 0;
1498
destruct_player[i].score = 0;
1499
destruct_player[i].aiMemory.c_Angle = 0;
1500
destruct_player[i].aiMemory.c_Power = 0;
1501
destruct_player[i].aiMemory.c_Fire = 0;
1502
destruct_player[i].aiMemory.c_noDown = 0;
1503
memcpy(destruct_player[i].keys.Config, defaultKeyConfig[i], sizeof(destruct_player[i].keys.Config));
1506
static void DE_ResetWeapons( void )
1511
for (i = 0; i < config.max_shots; i++)
1512
shotRec[i].isAvailable = true;
1514
for (i = 0; i < config.max_explosions; i++)
1515
exploRec[i].isAvailable = true;
1517
static void DE_ResetLevel( void )
1519
/* Okay, let's prep the arena */
1523
JE_generateTerrain();
1526
static void DE_ResetAI( void )
1529
struct destruct_unit_s * ptr;
1532
for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++)
1534
if (destruct_player[i].is_cpu == false) { continue; }
1535
ptr = destruct_player[i].unit;
1537
for( j = 0; j < config.max_installations; j++, ptr++)
1539
if(DE_isValidUnit(ptr) == false)
1542
if (systemAngle[ptr->unitType] || ptr->unitType == UNIT_HELI)
1543
ptr->angle = M_PI_4;
1547
ptr->power = (ptr->unitType == UNIT_LASER) ? 6 : 4;
1549
if (world.mapFlags & MAP_WALLS)
1550
ptr->shotType = defaultCpuWeaponB[ptr->unitType];
1552
ptr->shotType = defaultCpuWeapon[ptr->unitType];
1556
static void DE_ResetActions( void )
1561
for(i = 0; i < MAX_PLAYERS; i++)
1562
{ /* Zero it all. A memset would do the trick */
1563
memset(&(destruct_player[i].moves), 0, sizeof(destruct_player[i].moves));
1568
* Runs one tick. One tick involves handling physics, drawing crap,
1569
* moving projectiles and explosions, and getting input.
1570
* Returns true while the game is running or false if the game is
1573
static enum de_state_t DE_RunTick( void )
1575
static unsigned int endDelay;
1580
memset(soundQueue, 0, sizeof(soundQueue));
1581
JE_tempScreenChecking();
1584
DE_RunTickCycleDeadUnits();
1587
DE_RunTickGravity();
1588
DE_RunTickAnimate();
1589
DE_RunTickDrawWalls();
1590
DE_RunTickExplosions();
1593
DE_RunTickDrawCrosshairs();
1594
DE_RunTickDrawHUD();
1597
if (destructFirstTime)
1599
fade_palette(colors, 25, 0, 255);
1600
destructFirstTime = false;
1604
DE_RunTickGetInput();
1611
return(STATE_RELOAD);
1614
else if ( DE_RunTickCheckEndgame() == true)
1619
DE_RunTickPlaySounds();
1621
/* The rest of this cruft needs to be put in appropriate sections */
1622
if (keysactive[SDLK_F10])
1624
destruct_player[PLAYER_LEFT].is_cpu = !destruct_player[PLAYER_LEFT].is_cpu;
1625
keysactive[SDLK_F10] = false;
1627
if (keysactive[SDLK_F11])
1629
destruct_player[PLAYER_RIGHT].is_cpu = !destruct_player[PLAYER_RIGHT].is_cpu;
1630
keysactive[SDLK_F11] = false;
1632
if (keysactive[SDLK_p])
1635
keysactive[lastkey_sym] = false;
1638
if (keysactive[SDLK_F1])
1641
keysactive[lastkey_sym] = false;
1646
if (keysactive[SDLK_ESCAPE])
1648
keysactive[SDLK_ESCAPE] = false;
1649
return(STATE_INIT); /* STATE_INIT drops us to the mode select */
1652
if (keysactive[SDLK_BACKSPACE])
1654
keysactive[SDLK_BACKSPACE] = false;
1655
return(STATE_RELOAD); /* STATE_RELOAD creates a new map */
1658
return(STATE_CONTINUE);
1663
* Handles something that we do once per tick, such as
1664
* track ammo and move asplosions.
1666
static void DE_RunTickCycleDeadUnits( void )
1669
struct destruct_unit_s * unit;
1672
/* This code automatically switches the active unit if it is destroyed
1673
* and skips over the useless satellite */
1674
for (i = 0; i < MAX_PLAYERS; i++)
1676
if (destruct_player[i].unitsRemaining == 0) { continue; }
1678
unit = &(destruct_player[i].unit[destruct_player[i].unitSelected]);
1679
while(DE_isValidUnit(unit) == false
1680
|| unit->shotType == SHOT_INVALID)
1682
destruct_player[i].unitSelected++;
1684
if (destruct_player[i].unitSelected >= config.max_installations)
1686
destruct_player[i].unitSelected = 0;
1687
unit = destruct_player[i].unit;
1692
static void DE_RunTickGravity( void )
1695
struct destruct_unit_s * unit;
1698
for (i = 0; i < MAX_PLAYERS; i++)
1701
unit = destruct_player[i].unit;
1702
for (j = 0; j < config.max_installations; j++, unit++)
1704
if (DE_isValidUnit(unit) == false) /* invalid unit */
1707
switch(unit->unitType)
1709
case UNIT_SATELLITE: /* satellites don't fall down */
1714
if (unit->isYInAir == true) /* unit is falling down, at least in theory */
1716
DE_GravityFlyUnit(unit);
1719
/* else fall through and treat as a normal unit */
1722
DE_GravityLowerUnit(unit);
1725
/* Draw the unit. */
1726
DE_GravityDrawUnit(i, unit);
1730
static void DE_GravityDrawUnit( enum de_player_t team, struct destruct_unit_s * unit )
1732
unsigned int anim_index;
1735
anim_index = GraphicBase[team][unit->unitType] + unit->ani_frame;
1736
if (unit->unitType == UNIT_HELI)
1738
/* Adjust animation index if we are travelling right or left. */
1739
if (unit->lastMove < -2)
1741
else if (unit->lastMove > 2)
1744
else /* This handles our cannons and the like */
1746
anim_index += floorf(unit->angle * 9.99f / M_PI);
1749
blit_sprite2(VGAScreen, unit->unitX, roundf(unit->unitY) - 13, eShapes[0], anim_index);
1751
static void DE_GravityLowerUnit( struct destruct_unit_s * unit )
1753
/* units fall at a constant speed. The heli is an odd case though;
1754
* we simply give it a downward velocity, but due to a buggy implementation
1755
* the chopper didn't lower until you tried to fly it up. Tyrian 2000 fixes
1756
* this by not making the chopper a special case. I've decided to actually
1757
* mix both; the chopper is given a slight downward acceleration (simulating
1758
* a 'rocky' takeoff), and it is lowered like a regular unit, but not as
1761
if(unit->unitY < 199) { /* checking takes time, don't check if it's at the bottom */
1762
if (JE_stabilityCheck(unit->unitX, roundf(unit->unitY)))
1764
switch(unit->unitType)
1767
unit->unitYMov = 1.5f;
1768
unit->unitY += 0.2f;
1775
if (unit->unitY > 199) /* could be possible */
1780
static void DE_GravityFlyUnit( struct destruct_unit_s * unit )
1782
if (unit->unitY + unit->unitYMov > 199) /* would hit bottom of screen */
1786
unit->isYInAir = false;
1790
/* move the unit and alter acceleration */
1791
unit->unitY += unit->unitYMov;
1792
if (unit->unitY < 24) /* This stops units from going above the screen */
1798
if (unit->unitType == UNIT_HELI) /* helicopters fall more slowly */
1799
unit->unitYMov += 0.0001f;
1801
unit->unitYMov += 0.03f;
1803
if (!JE_stabilityCheck(unit->unitX, roundf(unit->unitY)))
1806
unit->isYInAir = false;
1809
static void DE_RunTickAnimate( void )
1812
struct destruct_unit_s * ptr;
1815
for (p = 0; p < MAX_PLAYERS; ++p)
1817
ptr = destruct_player[p].unit;
1818
for (u = 0; u < config.max_installations; ++u, ++ptr)
1820
/* Don't mess with any unit that is unallocated
1821
* or doesn't animate and is set to frame 0 */
1822
if(DE_isValidUnit(ptr) == false) { continue; }
1823
if(systemAni[ptr->unitType] == false && ptr->ani_frame == 0) { continue; }
1825
if (++(ptr->ani_frame) > 3)
1832
static void DE_RunTickDrawWalls( void )
1837
for (i = 0; i < config.max_walls; i++)
1839
if (world.mapWalls[i].wallExist)
1841
blit_sprite2(VGAScreen, world.mapWalls[i].wallX, world.mapWalls[i].wallY, eShapes[0], 42);
1845
static void DE_RunTickExplosions( void )
1848
int tempPosX, tempPosY;
1852
/* Run through all open explosions. They are not sorted in any way */
1853
for (i = 0; i < config.max_explosions; i++)
1855
if (exploRec[i].isAvailable == true) { continue; } /* Nothing to do */
1857
for (j = 0; j < exploRec[i].explofill; j++)
1859
/* An explosion is comprised of multiple 'flares' that fan out.
1860
Calculate where this 'flare' will end up */
1861
tempRadian = mt_rand_lt1() * (2 * M_PI);
1862
tempPosY = exploRec[i].y + roundf(cosf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth);
1863
tempPosX = exploRec[i].x + roundf(sinf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth);
1865
/* Our game allows explosions to wrap around. This looks to have
1866
* originally been a bug that was left in as being fun, but we are
1867
* going to replicate it w/o risking out of bound arrays. */
1869
while(tempPosX < 0) { tempPosX += 320; }
1870
while(tempPosX > 320) { tempPosX -= 320; }
1872
/* We don't draw our explosion if it's out of bounds vertically */
1873
if (tempPosY >= 200 || tempPosY <= 15) { continue; }
1875
/* And now the drawing. There are only two types of explosions
1876
* right now; dirt and flares. Dirt simply draws a brown pixel;
1877
* flares explode and have a star formation. */
1878
switch(exploRec[i].exploType)
1881
((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch] = PIXEL_DIRT;
1885
JE_superPixel(tempPosX, tempPosY);
1886
DE_TestExplosionCollision(tempPosX, tempPosY);
1895
/* Widen the explosion and delete it if necessary. */
1896
exploRec[i].explowidth++;
1897
if (exploRec[i].explowidth == exploRec[i].explomax)
1899
exploRec[i].isAvailable = true;
1903
static void DE_TestExplosionCollision( unsigned int PosX, unsigned int PosY)
1906
struct destruct_unit_s * unit;
1909
for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++)
1911
unit = destruct_player[i].unit;
1912
for (j = 0; j < config.max_installations; j++, unit++)
1914
if (DE_isValidUnit(unit) == true
1915
&& PosX > unit->unitX && PosX < unit->unitX + 11
1916
&& PosY < unit->unitY && PosY > unit->unitY - 11)
1919
if (unit->health <= 0)
1921
DE_DestroyUnit(i, unit);
1927
static void DE_DestroyUnit( enum de_player_t playerID, struct destruct_unit_s * unit )
1929
/* This function call was an evil evil piece of brilliance before. Go on.
1930
* Look at the older revisions. It passed the result of a comparison.
1931
* MULTIPLIED. This is at least a little clearer... */
1932
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. */
1934
if (unit->unitType != UNIT_SATELLITE) /* increment score */
1935
{ /* todo: change when teams are created. Hacky kludge for now.*/
1936
destruct_player[playerID].unitsRemaining--;
1937
destruct_player[((playerID == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT)].score++;
1941
static void DE_RunTickShots( void )
1943
unsigned int i, j, k;
1944
unsigned int tempTrails;
1945
unsigned int tempPosX, tempPosY;
1946
struct destruct_unit_s * unit;
1949
for (i = 0; i < config.max_shots; i++)
1951
if (shotRec[i].isAvailable == true) { continue; } /* Nothing to do */
1953
/* Move the shot. Simple displacement */
1954
shotRec[i].x += shotRec[i].xmov;
1955
shotRec[i].y += shotRec[i].ymov;
1957
/* If the shot can bounce off the map, bounce it */
1958
if (shotBounce[shotRec[i].shottype])
1960
if (shotRec[i].y > 199 || shotRec[i].y < 14)
1962
shotRec[i].y -= shotRec[i].ymov;
1963
shotRec[i].ymov = -shotRec[i].ymov;
1965
if (shotRec[i].x < 1 || shotRec[i].x > 318)
1967
shotRec[i].x -= shotRec[i].xmov;
1968
shotRec[i].xmov = -shotRec[i].xmov;
1971
else /* If it cannot, apply normal physics */
1973
shotRec[i].ymov += 0.05f; /* add gravity */
1975
if (shotRec[i].y > 199) /* We hit the floor */
1977
shotRec[i].y -= shotRec[i].ymov;
1978
shotRec[i].ymov = -shotRec[i].ymov * 0.8f; /* bounce at reduced velocity */
1980
/* Don't allow a bouncing shot to bounce straight up and down */
1981
if (shotRec[i].xmov == 0)
1983
shotRec[i].xmov += mt_rand_lt1() - 0.5f;
1988
/* Shot has gone out of bounds. Eliminate it. */
1989
if (shotRec[i].x > 318 || shotRec[i].x < 1)
1991
shotRec[i].isAvailable = true;
1995
/* Now check for collisions. */
1997
/* Don't bother checking for collisions above the map :) */
1998
if (shotRec[i].y <= 14)
2001
tempPosX = roundf(shotRec[i].x);
2002
tempPosY = roundf(shotRec[i].y);
2004
/*Check building hits*/
2005
for(j = 0; j < MAX_PLAYERS; j++)
2007
unit = destruct_player[j].unit;
2008
for(k = 0; k < config.max_installations; k++, unit++)
2010
if (DE_isValidUnit(unit) == false)
2013
if (tempPosX > unit->unitX && tempPosX < unit->unitX + 11
2014
&& tempPosY < unit->unitY && tempPosY > unit->unitY - 13)
2016
shotRec[i].isAvailable = true;
2017
JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype);
2022
tempTrails = (shotColor[shotRec[i].shottype] << 4) - 3;
2023
JE_pixCool(tempPosX, tempPosY, tempTrails);
2025
/*Draw the shot trail (if applicable) */
2026
switch (shotTrail[shotRec[i].shottype])
2031
DE_DrawTrails( &(shotRec[i]), 2, 4, tempTrails - 3 );
2034
DE_DrawTrails( &(shotRec[i]), 4, 3, tempTrails - 1 );
2038
/* Bounce off of or destroy walls */
2039
for (j = 0; j < config.max_walls; j++)
2041
if (world.mapWalls[j].wallExist == true
2042
&& tempPosX >= world.mapWalls[j].wallX && tempPosX <= world.mapWalls[j].wallX + 11
2043
&& tempPosY >= world.mapWalls[j].wallY && tempPosY <= world.mapWalls[j].wallY + 14)
2045
if (demolish[shotRec[i].shottype])
2047
/* Blow up the wall and remove the shot. */
2048
world.mapWalls[j].wallExist = false;
2049
shotRec[i].isAvailable = true;
2050
JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype);
2055
/* Otherwise, bounce. */
2056
if (shotRec[i].x - shotRec[i].xmov < world.mapWalls[j].wallX
2057
|| shotRec[i].x - shotRec[i].xmov > world.mapWalls[j].wallX + 11)
2059
shotRec[i].xmov = -shotRec[i].xmov;
2061
if (shotRec[i].y - shotRec[i].ymov < world.mapWalls[j].wallY
2062
|| shotRec[i].y - shotRec[i].ymov > world.mapWalls[j].wallY + 14)
2064
if (shotRec[i].ymov < 0)
2065
shotRec[i].ymov = -shotRec[i].ymov;
2067
shotRec[i].ymov = -shotRec[i].ymov * 0.8f;
2070
tempPosX = roundf(shotRec[i].x);
2071
tempPosY = roundf(shotRec[i].y);
2076
/* Our last collision check, at least for now. We hit dirt. */
2077
if((((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch]) == PIXEL_DIRT)
2079
shotRec[i].isAvailable = true;
2080
JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype);
2085
static void DE_DrawTrails( struct destruct_shot_s * shot, unsigned int count, unsigned int decay, unsigned int startColor )
2090
for (i = count-1; i >= 0; i--) /* going in reverse is important as it affects how we draw */
2092
if (shot->trailc[i] > 0 && shot->traily[i] > 12) /* If it exists and if it's not out of bounds, draw it. */
2094
JE_pixCool(shot->trailx[i], shot->traily[i], shot->trailc[i]);
2097
if (i == 0) /* The first trail we create. */
2099
shot->trailx[i] = roundf(shot->x);
2100
shot->traily[i] = roundf(shot->y);
2101
shot->trailc[i] = startColor;
2103
else /* The newer trails decay into the older trails.*/
2105
shot->trailx[i] = shot->trailx[i-1];
2106
shot->traily[i] = shot->traily[i-1];
2107
if (shot->trailc[i-1] > 0)
2109
shot->trailc[i] = shot->trailc[i-1] - decay;
2114
static void DE_RunTickAI( void )
2117
struct destruct_player_s * ptrPlayer, * ptrTarget;
2118
struct destruct_unit_s * ptrUnit, * ptrCurUnit;
2121
for (i = 0; i < MAX_PLAYERS; i++)
2123
ptrPlayer = &(destruct_player[i]);
2124
if (ptrPlayer->is_cpu == false)
2130
/* I've been thinking, purely hypothetically, about what it would take
2131
* to have multiple computer opponents. The answer? A lot of crap
2132
* and a 'target' variable in the destruct_player struct. */
2134
if (j >= MAX_PLAYERS)
2139
ptrTarget = &(destruct_player[j]);
2140
ptrCurUnit = &(ptrPlayer->unit[ptrPlayer->unitSelected]);
2143
/* This is the start of the original AI. Heh. AI. */
2145
if (ptrPlayer->aiMemory.c_noDown > 0)
2146
ptrPlayer->aiMemory.c_noDown--;
2148
/* Until all structs are properly divvied up this must only apply to player1 */
2149
if (mt_rand() % 100 > 80)
2151
ptrPlayer->aiMemory.c_Angle += (mt_rand() % 3) - 1;
2153
if (ptrPlayer->aiMemory.c_Angle > 1)
2154
ptrPlayer->aiMemory.c_Angle = 1;
2156
if (ptrPlayer->aiMemory.c_Angle < -1)
2157
ptrPlayer->aiMemory.c_Angle = -1;
2159
if (mt_rand() % 100 > 90)
2161
if (ptrPlayer->aiMemory.c_Angle > 0 && ptrCurUnit->angle > (M_PI_2) - (M_PI / 9))
2162
ptrPlayer->aiMemory.c_Angle = 0;
2164
if (ptrPlayer->aiMemory.c_Angle < 0 && ptrCurUnit->angle < M_PI / 8)
2165
ptrPlayer->aiMemory.c_Angle = 0;
2168
if (mt_rand() % 100 > 93)
2170
ptrPlayer->aiMemory.c_Power += (mt_rand() % 3) - 1;
2172
if (ptrPlayer->aiMemory.c_Power > 1)
2173
ptrPlayer->aiMemory.c_Power = 1;
2175
if (ptrPlayer->aiMemory.c_Power < -1)
2176
ptrPlayer->aiMemory.c_Power = -1;
2178
if (mt_rand() % 100 > 90)
2180
if (ptrPlayer->aiMemory.c_Power > 0 && ptrCurUnit->power > 4)
2181
ptrPlayer->aiMemory.c_Power = 0;
2183
if (ptrPlayer->aiMemory.c_Power < 0 && ptrCurUnit->power < 3)
2184
ptrPlayer->aiMemory.c_Power = 0;
2186
if (ptrCurUnit->power < 2)
2187
ptrPlayer->aiMemory.c_Power = 1;
2190
// prefer helicopter
2191
ptrUnit = ptrPlayer->unit;
2192
for (j = 0; j < config.max_installations; j++, ptrUnit++)
2194
if (DE_isValidUnit(ptrUnit) && ptrUnit->unitType == UNIT_HELI)
2196
ptrPlayer->unitSelected = j;
2201
if (ptrCurUnit->unitType == UNIT_HELI)
2203
if (ptrCurUnit->isYInAir == false)
2205
ptrPlayer->aiMemory.c_Power = 1;
2207
if (mt_rand() % ptrCurUnit->unitX > 100)
2209
ptrPlayer->aiMemory.c_Power = 1;
2211
if (mt_rand() % 240 > ptrCurUnit->unitX)
2213
ptrPlayer->moves.actions[MOVE_RIGHT] = true;
2215
else if ((mt_rand() % 20) + 300 < ptrCurUnit->unitX)
2217
ptrPlayer->moves.actions[MOVE_LEFT] = true;
2219
else if (mt_rand() % 30 == 1)
2221
ptrPlayer->aiMemory.c_Angle = (mt_rand() % 3) - 1;
2223
if (ptrCurUnit->unitX > 295 && ptrCurUnit->lastMove > 1)
2225
ptrPlayer->moves.actions[MOVE_LEFT] = true;
2226
ptrPlayer->moves.actions[MOVE_RIGHT] = false;
2228
if (ptrCurUnit->unitType != UNIT_HELI || ptrCurUnit->lastMove > 3 || (ptrCurUnit->unitX > 160 && ptrCurUnit->lastMove > -3))
2230
if (mt_rand() % (int)roundf(ptrCurUnit->unitY) < 150 && ptrCurUnit->unitYMov < 0.01f && (ptrCurUnit->unitX < 160 || ptrCurUnit->lastMove < 2))
2232
ptrPlayer->moves.actions[MOVE_FIRE] = true;
2234
ptrPlayer->aiMemory.c_noDown = (5 - abs(ptrCurUnit->lastMove)) * (5 - abs(ptrCurUnit->lastMove)) + 3;
2235
ptrPlayer->aiMemory.c_Power = 1;
2237
ptrPlayer->moves.actions[MOVE_FIRE] = false;
2240
ptrUnit = ptrTarget->unit;
2241
for (j = 0; j < config.max_installations; j++, ptrUnit++)
2243
if (abs(ptrUnit->unitX - ptrCurUnit->unitX) < 8)
2245
/* I get it. This makes helicoptors hover over
2247
if (ptrUnit->unitType == UNIT_SATELLITE)
2249
ptrPlayer->moves.actions[MOVE_FIRE] = false;
2253
ptrPlayer->moves.actions[MOVE_LEFT] = false;
2254
ptrPlayer->moves.actions[MOVE_RIGHT] = false;
2255
if (ptrCurUnit->lastMove < -1)
2257
ptrCurUnit->lastMove++;
2259
else if (ptrCurUnit->lastMove > 1)
2261
ptrCurUnit->lastMove--;
2267
ptrPlayer->moves.actions[MOVE_FIRE] = 1;
2270
if (mt_rand() % 200 > 198)
2272
ptrPlayer->moves.actions[MOVE_CHANGE] = true;
2273
ptrPlayer->aiMemory.c_Angle = 0;
2274
ptrPlayer->aiMemory.c_Power = 0;
2275
ptrPlayer->aiMemory.c_Fire = 0;
2278
if (mt_rand() % 100 > 98 || ptrCurUnit->shotType == SHOT_TRACER)
2279
{ /* Clearly the CPU doesn't like the tracer :) */
2280
ptrPlayer->moves.actions[MOVE_CYDN] = true;
2282
if (ptrPlayer->aiMemory.c_Angle > 0)
2284
ptrPlayer->moves.actions[MOVE_LEFT] = true;
2286
if (ptrPlayer->aiMemory.c_Angle < 0)
2288
ptrPlayer->moves.actions[MOVE_RIGHT] = true;
2290
if (ptrPlayer->aiMemory.c_Power > 0)
2292
ptrPlayer->moves.actions[MOVE_UP] = true;
2294
if (ptrPlayer->aiMemory.c_Power < 0 && ptrPlayer->aiMemory.c_noDown == 0)
2296
ptrPlayer->moves.actions[MOVE_DOWN] = true;
2298
if (ptrPlayer->aiMemory.c_Fire > 0)
2300
ptrPlayer->moves.actions[MOVE_FIRE] = true;
2303
if (ptrCurUnit->unitYMov < -0.1f && ptrCurUnit->unitType == UNIT_HELI)
2305
ptrPlayer->moves.actions[MOVE_FIRE] = false;
2308
/* This last hack was down in the processing section.
2309
* What exactly it was doing there I do not know */
2310
if(ptrCurUnit->unitType == UNIT_LASER || ptrCurUnit->isYInAir == true) {
2311
ptrPlayer->aiMemory.c_Power = 0;
2315
static void DE_RunTickDrawCrosshairs( void )
2318
int tempPosX, tempPosY;
2320
struct destruct_unit_s * curUnit;
2323
/* Draw the crosshairs. Most vehicles aim left or right. Helis can aim
2324
* either way and this must be accounted for.
2326
for (i = 0; i < MAX_PLAYERS; i++)
2328
direction = (i == PLAYER_LEFT) ? -1 : 1;
2329
curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]);
2331
if (curUnit->unitType == UNIT_HELI)
2333
tempPosX = curUnit->unitX + roundf(0.1f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove) + 5;
2334
tempPosY = roundf(curUnit->unitY) + 1;
2336
tempPosX = roundf(curUnit->unitX + 6 - cosf(curUnit->angle) * (curUnit->power * 8 + 7) * direction);
2337
tempPosY = roundf(curUnit->unitY - 7 - sinf(curUnit->angle) * (curUnit->power * 8 + 7));
2340
/* Draw it. Clip away from the HUD though. */
2348
JE_pix(VGAScreen, tempPosX, tempPosY - 2, 3);
2350
/* Middle three pixels */
2351
JE_pix(VGAScreen, tempPosX + 3, tempPosY, 3);
2352
JE_pix(VGAScreen, tempPosX, tempPosY, 14);
2353
JE_pix(VGAScreen, tempPosX - 3, tempPosY, 3);
2356
JE_pix(VGAScreen, tempPosX, tempPosY + 2, 3);
2360
static void DE_RunTickDrawHUD( void )
2363
unsigned int startX;
2364
char tempstr[16]; /* Max size needed: 16 assuming 10 digit int max. */
2365
struct destruct_unit_s * curUnit;
2368
for (i = 0; i < MAX_PLAYERS; i++)
2370
curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]);
2371
startX = ((i == PLAYER_LEFT) ? 0 : 320 - 150);
2373
fill_rectangle_xy(VGAScreen, startX + 5, 3, startX + 14, 8, 241);
2374
JE_rectangle(VGAScreen, startX + 4, 2, startX + 15, 9, 242);
2375
JE_rectangle(VGAScreen, startX + 3, 1, startX + 16, 10, 240);
2376
fill_rectangle_xy(VGAScreen, startX + 18, 3, startX + 140, 8, 241);
2377
JE_rectangle(VGAScreen, startX + 17, 2, startX + 143, 9, 242);
2378
JE_rectangle(VGAScreen, startX + 16, 1, startX + 144, 10, 240);
2380
blit_sprite2(VGAScreen, startX + 4, 0, eShapes[0], 191 + curUnit->shotType);
2382
JE_outText (VGAScreen, startX + 20, 3, weaponNames[curUnit->shotType], 15, 2);
2383
sprintf (tempstr, "dmg~%d~", curUnit->health);
2384
JE_outText (VGAScreen, startX + 75, 3, tempstr, 15, 0);
2385
sprintf (tempstr, "pts~%d~", destruct_player[i].score);
2386
JE_outText (VGAScreen, startX + 110, 3, tempstr, 15, 0);
2389
static void DE_RunTickGetInput( void )
2391
unsigned int player_index, key_index, slot_index;
2394
/* destruct_player.keys holds our key config. Players will eventually be
2395
* allowed to can change their key mappings. destruct_player.moves and
2396
* destruct_player.keys line up; rather than manually checking left and
2397
* right we can just loop through the indexes and set the actions as
2399
service_SDL_events(true);
2401
for(player_index = 0; player_index < MAX_PLAYERS; player_index++)
2403
for(key_index = 0; key_index < MAX_KEY; key_index++)
2405
for(slot_index = 0; slot_index < MAX_KEY_OPTIONS; slot_index++)
2407
key = destruct_player[player_index].keys.Config[key_index][slot_index];
2408
if(key == SDLK_UNKNOWN) { break; }
2409
if(keysactive[key] == true)
2411
/* The right key was clearly pressed */
2412
destruct_player[player_index].moves.actions[key_index] = true;
2414
/* Some keys we want to toggle afterwards */
2415
if(key_index == KEY_CHANGE ||
2416
key_index == KEY_CYUP ||
2417
key_index == KEY_CYDN)
2419
keysactive[key] = false;
2427
static void DE_ProcessInput( void )
2431
unsigned int player_index;
2432
struct destruct_unit_s * curUnit;
2435
for (player_index = 0; player_index < MAX_PLAYERS; player_index++)
2437
if (destruct_player[player_index].unitsRemaining <= 0) { continue; }
2439
direction = (player_index == PLAYER_LEFT) ? -1 : 1;
2440
curUnit = &(destruct_player[player_index].unit[destruct_player[player_index].unitSelected]);
2442
if (systemAngle[curUnit->unitType] == true) /* selected unit may change shot angle */
2444
if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true)
2446
(player_index == PLAYER_LEFT) ? DE_RaiseAngle(curUnit) : DE_LowerAngle(curUnit);
2448
if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true)
2450
(player_index == PLAYER_LEFT) ? DE_LowerAngle(curUnit) : DE_RaiseAngle(curUnit);
2453
} else if (curUnit->unitType == UNIT_HELI) {
2454
if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true && curUnit->unitX > 5)
2455
if (JE_stabilityCheck(curUnit->unitX - 5, roundf(curUnit->unitY)))
2457
if (curUnit->lastMove > -5)
2459
curUnit->lastMove--;
2462
if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY)))
2464
curUnit->isYInAir = true;
2467
if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true && curUnit->unitX < 305)
2469
if (JE_stabilityCheck(curUnit->unitX + 5, roundf(curUnit->unitY)))
2471
if (curUnit->lastMove < 5)
2473
curUnit->lastMove++;
2476
if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY)))
2478
curUnit->isYInAir = true;
2484
if (curUnit->unitType != UNIT_LASER)
2487
if (destruct_player[player_index].moves.actions[MOVE_UP] == true)
2489
if (curUnit->unitType == UNIT_HELI)
2491
curUnit->isYInAir = true;
2492
curUnit->unitYMov -= 0.1f;
2494
else if (curUnit->unitType == UNIT_JUMPER
2495
&& curUnit->isYInAir == false) {
2496
curUnit->unitYMov = -3;
2497
curUnit->isYInAir = true;
2500
DE_RaisePower(curUnit);
2504
if (destruct_player[player_index].moves.actions[MOVE_DOWN] == true)
2506
if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == true)
2508
curUnit->unitYMov += 0.1f;
2510
DE_LowerPower(curUnit);
2515
/*up/down weapon. These just cycle until a valid weapon is found */
2516
if (destruct_player[player_index].moves.actions[MOVE_CYUP] == true)
2518
DE_CycleWeaponUp(curUnit);
2520
if (destruct_player[player_index].moves.actions[MOVE_CYDN] == true)
2522
DE_CycleWeaponDown(curUnit);
2525
/* Change. Since change would change out curUnit pointer, let's just do it last.
2526
* Validity checking is performed at the beginning of the tick. */
2527
if (destruct_player[player_index].moves.actions[MOVE_CHANGE] == true)
2529
destruct_player[player_index].unitSelected++;
2530
if (destruct_player[player_index].unitSelected >= config.max_installations)
2532
destruct_player[player_index].unitSelected = 0;
2537
if (destruct_player[player_index].shotDelay > 0)
2539
destruct_player[player_index].shotDelay--;
2541
if (destruct_player[player_index].moves.actions[MOVE_FIRE] == true
2542
&& (destruct_player[player_index].shotDelay == 0))
2544
destruct_player[player_index].shotDelay = shotDelay[curUnit->shotType];
2546
switch(shotDirt[curUnit->shotType])
2552
DE_RunMagnet(player_index, curUnit);
2557
DE_MakeShot(player_index, curUnit, direction);
2567
static void DE_CycleWeaponUp( struct destruct_unit_s * unit )
2572
if (unit->shotType > SHOT_LAST)
2574
unit->shotType = SHOT_FIRST;
2576
} while (weaponSystems[unit->unitType][unit->shotType] == 0);
2578
static void DE_CycleWeaponDown( struct destruct_unit_s * unit )
2583
if (unit->shotType < SHOT_FIRST)
2585
unit->shotType = SHOT_LAST;
2587
} while (weaponSystems[unit->unitType][unit->shotType] == 0);
2591
static void DE_MakeShot( enum de_player_t curPlayer, const struct destruct_unit_s * curUnit, int direction )
2594
unsigned int shotIndex;
2597
/* First, find an empty shot struct we can use */
2600
if (i >= config.max_shots) { return; } /* no empty slots. Do nothing. */
2602
if (shotRec[i].isAvailable)
2608
if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == false)
2609
{ /* Helis can't fire when they are on the ground. */
2613
/* Play the firing sound */
2614
soundQueue[curPlayer] = shotSound[curUnit->shotType];
2616
/* Create our shot. Some units have differing logic here */
2617
switch (curUnit->unitType)
2621
shotRec[shotIndex].x = curUnit->unitX + curUnit->lastMove * 2 + 5;
2622
shotRec[shotIndex].xmov = 0.02f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove;
2624
/* If we are trying in vain to move up off the screen, act differently.*/
2625
if (destruct_player[curPlayer].moves.actions[MOVE_UP] && curUnit->unitY < 30)
2627
shotRec[shotIndex].y = curUnit->unitY;
2628
shotRec[shotIndex].ymov = 0.1f;
2630
if (shotRec[shotIndex].xmov < 0)
2632
shotRec[shotIndex].xmov += 0.1f;
2634
else if (shotRec[shotIndex].xmov > 0)
2636
shotRec[shotIndex].xmov -= 0.1f;
2641
shotRec[shotIndex].y = curUnit->unitY + 1;
2642
shotRec[shotIndex].ymov = 0.5f + curUnit->unitYMov * 0.1f;
2646
case UNIT_JUMPER: /* Jumpers are normally only special for the left hand player. Bug? Or feature? */
2648
if(config.jumper_straight[curPlayer])
2650
/* This is identical to the default case.
2651
* I considered letting the switch fall through
2652
* but that's more confusing to people who aren't used
2653
* to that quirk of switch. */
2655
shotRec[shotIndex].x = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction;
2656
shotRec[shotIndex].y = curUnit->unitY - 7 - sinf(curUnit->angle) * 10;
2657
shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction;
2658
shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power;
2662
/* This is not identical to the default case. */
2664
shotRec[shotIndex].x = curUnit->unitX + 2;
2665
shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction;
2667
if (curUnit->isYInAir == true)
2669
shotRec[shotIndex].ymov = 1;
2670
shotRec[shotIndex].y = curUnit->unitY + 2;
2672
shotRec[shotIndex].ymov = -2;
2673
shotRec[shotIndex].y = curUnit->unitY - 12;
2680
shotRec[shotIndex].x = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction;
2681
shotRec[shotIndex].y = curUnit->unitY - 7 - sinf(curUnit->angle) * 10;
2682
shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction;
2683
shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power;
2687
/* Now set/clear out a few last details. */
2688
shotRec[shotIndex].isAvailable = false;
2690
shotRec[shotIndex].shottype = curUnit->shotType;
2691
//shotRec[shotIndex].shotdur = shotFuse[shotRec[shotIndex].shottype];
2693
shotRec[shotIndex].trailc[0] = 0;
2694
shotRec[shotIndex].trailc[1] = 0;
2695
shotRec[shotIndex].trailc[2] = 0;
2696
shotRec[shotIndex].trailc[3] = 0;
2698
static void DE_RunMagnet( enum de_player_t curPlayer, struct destruct_unit_s * magnet )
2701
enum de_player_t curEnemy;
2703
struct destruct_unit_s * enemyUnit;
2706
curEnemy = (curPlayer == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT;
2707
direction = (curPlayer == PLAYER_LEFT) ? -1 : 1;
2709
/* Push all shots that are in front of the magnet */
2710
for (i = 0; i < config.max_shots; i++)
2712
if (shotRec[i].isAvailable == false)
2714
if ((curPlayer == PLAYER_LEFT && shotRec[i].x > magnet->unitX)
2715
|| (curPlayer == PLAYER_RIGHT && shotRec[i].x < magnet->unitX))
2717
shotRec[i].xmov += magnet->power * 0.1f * -direction;
2722
enemyUnit = destruct_player[curEnemy].unit;
2723
for (i = 0; i < config.max_installations; i++, enemyUnit++) /* magnets push coptors */
2725
if (DE_isValidUnit(enemyUnit)
2726
&& enemyUnit->unitType == UNIT_HELI
2727
&& enemyUnit->isYInAir == true)
2729
if ((curEnemy == PLAYER_RIGHT && destruct_player[curEnemy].unit[i].unitX + 11 < 318)
2730
|| (curEnemy == PLAYER_LEFT && destruct_player[curEnemy].unit[i].unitX > 1))
2732
enemyUnit->unitX -= 2 * direction;
2736
magnet->ani_frame = 1;
2738
static void DE_RaiseAngle( struct destruct_unit_s * unit )
2740
unit->angle += 0.01f;
2741
if (unit->angle > M_PI_2 - 0.01f)
2743
unit->angle = M_PI_2 - 0.01f;
2746
static void DE_LowerAngle( struct destruct_unit_s * unit )
2748
unit->angle -= 0.01f;
2749
if (unit->angle < 0)
2754
static void DE_RaisePower( struct destruct_unit_s * unit )
2756
unit->power += 0.05f;
2757
if (unit->power > 5)
2762
static void DE_LowerPower( struct destruct_unit_s * unit )
2764
unit->power -= 0.05f;
2765
if (unit->power < 1)
2773
* Returns true if the unit's health is above 0 and false
2774
* otherwise. This mainly exists because the 'health' var
2775
* serves two roles and that can get confusing.
2777
static inline bool DE_isValidUnit( struct destruct_unit_s * unit )
2779
return(unit->health > 0);
2783
static bool DE_RunTickCheckEndgame( void )
2785
if (destruct_player[PLAYER_LEFT].unitsRemaining == 0)
2787
destruct_player[PLAYER_RIGHT].score += ModeScore[PLAYER_LEFT][world.destructMode];
2788
soundQueue[7] = V_CLEARED_PLATFORM;
2791
if (destruct_player[PLAYER_RIGHT].unitsRemaining == 0)
2793
destruct_player[PLAYER_LEFT].score += ModeScore[PLAYER_RIGHT][world.destructMode];
2794
soundQueue[7] = V_CLEARED_PLATFORM;
2799
static void DE_RunTickPlaySounds( void )
2801
unsigned int i, tempSampleIndex, tempVolume;
2804
for (i = 0; i < COUNTOF(soundQueue); i++)
2806
if (soundQueue[i] != S_NONE)
2808
tempSampleIndex = soundQueue[i];
2811
tempVolume = fxPlayVol;
2815
tempVolume = fxPlayVol / 2;
2818
JE_multiSamplePlay(digiFx[tempSampleIndex-1], fxSize[tempSampleIndex-1], i, tempVolume);
2819
soundQueue[i] = S_NONE;
2824
static void JE_pixCool( unsigned int x, unsigned int y, Uint8 c )
2826
JE_pix(VGAScreen, x, y, c);
2827
JE_pix(VGAScreen, x - 1, y, c - 2);
2828
JE_pix(VGAScreen, x + 1, y, c - 2);
2829
JE_pix(VGAScreen, x, y - 1, c - 2);
2830
JE_pix(VGAScreen, x, y + 1, c - 2);