Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
9169 | turbocat | 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 |
||
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 | }>>>>>>>>>>>>=>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>=>=>>><>>>>>=>>>>>=>>>>>=>>>>>>>>>>>>>>>>>>>>>=>>>>>>>>>>>>>>>>=>>=>>>>>>>=>=>=>>>>>>>>=>>=>>>>>>>>>>>>>> |