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 #include "config.h"
20 #include "editship.h"
21 #include "episodes.h"
22 #include "joystick.h"
23 #include "lds_play.h"
24 #include "loudness.h"
25 #include "mainint.h"
26 #include "mouse.h"
27 #include "mtrand.h"
28 #include "network.h"
29 #include "nortsong.h"
30 #include "nortvars.h"
31 #include "opentyr.h"
32 #include "shots.h"
33 #include "sprite.h"
34 #include "varz.h"
35 #include "vga256d.h"
36 #include "video.h"
37 
38 JE_integer tempDat, tempDat2, tempDat3;
39 
40 const JE_byte SANextShip[SA + 2] /* [0..SA + 1] */ = { 3, 9, 6, 2, 5, 1, 4, 3, 7 }; // 0 -> 3 -> 2 -> 6 -> 4 -> 5 -> 1 -> 9 -> 7
41 const JE_word SASpecialWeapon[SA] /* [1..SA] */  = { 7, 8, 9, 10, 11, 12, 13 };
42 const JE_word SASpecialWeaponB[SA] /* [1..SA] */ = {37, 6, 15, 40, 16, 14, 41 };
43 const JE_byte SAShip[SA] /* [1..SA] */ = { 3, 1, 5, 10, 2, 11, 12 };
44 const JE_word SAWeapon[SA][5] /* [1..SA, 1..5] */ =
45 {  /*  R  Bl  Bk  G   P */
46 	{  9, 31, 32, 33, 34 },  /* Stealth Ship */
47 	{ 19,  8, 22, 41, 34 },  /* StormWind    */
48 	{ 27,  5, 20, 42, 31 },  /* Techno       */
49 	{ 15,  3, 28, 22, 12 },  /* Enemy        */
50 	{ 23, 35, 25, 14,  6 },  /* Weird        */
51 	{  2,  5, 21,  4,  7 },  /* Unknown      */
52 	{ 40, 38, 37, 41, 36 }   /* NortShip Z   */
53 };
54 
55 const JE_byte specialArcadeWeapon[PORT_NUM] /* [1..Portnum] */ =
56 {
57 	17,17,18,0,0,0,10,0,0,0,0,0,44,0,10,0,19,0,0,-0,0,0,0,0,0,0,
58 	-0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0
59 };
60 
61 const JE_byte optionSelect[16][3][2] /* [0..15, 1..3, 1..2] */ =
62 {	/*  MAIN    OPT    FRONT */
63 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
64 	{ { 1, 1},{16,16},{30,30} },  /*Single Shot*/
65 	{ { 2, 2},{29,29},{29,20} },  /*Dual Shot*/
66 	{ { 3, 3},{21,21},{12, 0} },  /*Charge Cannon*/
67 	{ { 4, 4},{18,18},{16,23} },  /*Vulcan*/
68 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
69 	{ { 6, 6},{29,16},{ 0,22} },  /*Super Missile*/
70 	{ { 7, 7},{19,19},{19,28} },  /*Atom Bomb*/
71 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
72 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
73 	{ {10,10},{21,21},{21,27} },  /*Mini Missile*/
74 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
75 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
76 	{ {13,13},{17,17},{13,26} },  /*MicroBomb*/
77 	{ { 0, 0},{ 0, 0},{ 0, 0} },  /**/
78 	{ {15,15},{15,16},{15,16} }   /*Post-It*/
79 };
80 
81 const JE_word PGR[21] /* [1..21] */ =
82 {
83 	4,
84 	1,2,3,
85 	41-21,57-21,73-21,89-21,105-21,
86 	121-21,137-21,153-21,
87 	151,151,151,151,73-21,73-21,1,2,4
88 	/*151,151,151*/
89 };
90 const JE_byte PAni[21] /* [1..21] */ = {1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1};
91 
92 const JE_word linkGunWeapons[38] /* [1..38] */ =
93 {
94 	0,0,0,0,0,0,0,0,444,445,446,447,0,448,449,0,0,0,0,0,450,451,0,506,0,564,
95 	  445,446,447,448,449,445,446,447,448,449,450,451
96 };
97 const JE_word chargeGunWeapons[38] /* [1..38] */ =
98 {
99 	0,0,0,0,0,0,0,0,476,458,464,482,0,488,470,0,0,0,0,0,494,500,0,528,0,558,
100 	  458,458,458,458,458,458,458,458,458,458,458,458
101 };
102 const JE_byte randomEnemyLaunchSounds[3] /* [1..3] */ = {13,6,26};
103 
104 /* YKS: Twiddle cheat sheet:
105  * 1: UP
106  * 2: DOWN
107  * 3: LEFT
108  * 4: RIGHT
109  * 5: UP+FIRE
110  * 6: DOWN+FIRE
111  * 7: LEFT+FIRE
112  * 8: RIGHT+FIRE
113  * 9: Release all keys (directions and fire)
114  */
115 const JE_byte keyboardCombos[26][8] /* [1..26, 1..8] */ =
116 {
117 	{ 2, 1,   2,   5, 137,           0, 0, 0}, /*Invulnerability*/
118 	{ 4, 3,   2,   5, 138,           0, 0, 0}, /*Atom Bomb*/
119 	{ 3, 4,   6, 139,             0, 0, 0, 0}, /*Seeker Bombs*/
120 	{ 2, 5, 142,               0, 0, 0, 0, 0}, /*Ice Blast*/
121 	{ 6, 2,   6, 143,             0, 0, 0, 0}, /*Auto Repair*/
122 	{ 6, 7,   5,   8,   6,   7,  5, 112     }, /*Spin Wave*/
123 	{ 7, 8, 101,               0, 0, 0, 0, 0}, /*Repulsor*/
124 	{ 1, 7,   6, 146,             0, 0, 0, 0}, /*Protron Field*/
125 	{ 8, 6,   7,   1, 120,           0, 0, 0}, /*Minefield*/
126 	{ 3, 6,   8,   5, 121,           0, 0, 0}, /*Post-It Blast*/
127 	{ 1, 2,   7,   8, 119,           0, 0, 0}, /*Drone Ship - TBC*/
128 	{ 3, 4,   3,   6, 123,           0, 0, 0}, /*Repair Player 2*/
129 	{ 6, 7,   5,   8, 124,           0, 0, 0}, /*Super Bomb - TBC*/
130 	{ 1, 6, 125,               0, 0, 0, 0, 0}, /*Hot Dog*/
131 	{ 9, 5, 126,               0, 0, 0, 0, 0}, /*Lightning UP      */
132 	{ 1, 7, 127,               0, 0, 0, 0, 0}, /*Lightning UP+LEFT */
133 	{ 1, 8, 128,               0, 0, 0, 0, 0}, /*Lightning UP+RIGHT*/
134 	{ 9, 7, 129,               0, 0, 0, 0, 0}, /*Lightning    LEFT */
135 	{ 9, 8, 130,               0, 0, 0, 0, 0}, /*Lightning    RIGHT*/
136 	{ 4, 2,   3,   5, 131,           0, 0, 0}, /*Warfly            */
137 	{ 3, 1,   2,   8, 132,           0, 0, 0}, /*FrontBlaster      */
138 	{ 2, 4,   5, 133,             0, 0, 0, 0}, /*Gerund            */
139 	{ 3, 4,   2,   8, 134,           0, 0, 0}, /*FireBomb          */
140 	{ 1, 4,   6, 135,             0, 0, 0, 0}, /*Indigo            */
141 	{ 1, 3,   6, 137,             0, 0, 0, 0}, /*Invulnerability [easier] */
142 	{ 1, 4,   3,   4,   7, 136,         0, 0}  /*D-Media Protron Drone    */
143 };
144 
145 const JE_byte shipCombosB[21] /* [1..21] */ =
146 	{15,16,17,18,19,20,21,22,23,24, 7, 8, 5,25,14, 4, 6, 3, 9, 2,26};
147   /*!! SUPER Tyrian !!*/
148 const JE_byte superTyrianSpecials[4] /* [1..4] */ = {1,2,4,5};
149 
150 const JE_byte shipCombos[14][3] /* [0..12, 1..3] */ =
151 {
152 	{ 5, 4, 7},  /*2nd Player ship*/
153 	{ 1, 2, 0},  /*USP Talon*/
154 	{14, 4, 0},  /*Super Carrot*/
155 	{ 4, 5, 0},  /*Gencore Phoenix*/
156 	{ 6, 5, 0},  /*Gencore Maelstrom*/
157 	{ 7, 8, 0},  /*MicroCorp Stalker*/
158 	{ 7, 9, 0},  /*MicroCorp Stalker-B*/
159 	{10, 3, 5},  /*Prototype Stalker-C*/
160 	{ 5, 8, 9},  /*Stalker*/
161 	{ 1, 3, 0},  /*USP Fang*/
162 	{ 7,16,17},  /*U-Ship*/
163 	{ 2,11,12},  /*1st Player ship*/
164 	{ 3, 8,10},  /*Nort ship*/
165 	{ 0, 0, 0}   // Dummy entry added for Stalker 21.126
166 };
167 
168 /*Street-Fighter Commands*/
169 JE_byte SFCurrentCode[2][21]; /* [1..2, 1..21] */
170 JE_byte SFExecuted[2]; /* [1..2] */
171 
172 /*Special General Data*/
173 JE_byte lvlFileNum;
174 JE_word maxEvent, eventLoc;
175 /*JE_word maxenemies;*/
176 JE_word tempBackMove, explodeMove; /*Speed of background movement*/
177 JE_byte levelEnd;
178 JE_word levelEndFxWait;
179 JE_shortint levelEndWarp;
180 JE_boolean endLevel, reallyEndLevel, waitToEndLevel, playerEndLevel,
181            normalBonusLevelCurrent, bonusLevelCurrent,
182            smallEnemyAdjust, readyToEndLevel, quitRequested;
183 
184 JE_byte newPL[10]; /* [0..9] */ /*Eventsys event 75 parameter*/
185 JE_word returnLoc;
186 JE_boolean returnActive;
187 JE_word galagaShotFreq;
188 JE_longint galagaLife;
189 
190 JE_boolean debug = false; /*Debug Mode*/
191 Uint32 debugTime, lastDebugTime;
192 JE_longint debugHistCount;
193 JE_real debugHist;
194 JE_word curLoc; /*Current Pixel location of background 1*/
195 
196 JE_boolean firstGameOver, gameLoaded, enemyStillExploding;
197 
198 
199 /* Destruction Ratio */
200 JE_word totalEnemy;
201 JE_word enemyKilled;
202 
203 /* Shape/Map Data - All in one Segment! */
204 struct JE_MegaDataType1 megaData1;
205 struct JE_MegaDataType2 megaData2;
206 struct JE_MegaDataType3 megaData3;
207 
208 /* Secret Level Display */
209 JE_byte flash;
210 JE_shortint flashChange;
211 JE_byte displayTime;
212 
213 /* Demo Stuff */
214 bool play_demo = false, record_demo = false, stopped_demo = false;
215 Uint8 demo_num = 0;
216 FILE *demo_file = NULL;
217 
218 Uint8 demo_keys, next_demo_keys;
219 Uint16 demo_keys_wait;
220 
221 /* Sound Effects Queue */
222 JE_byte soundQueue[8]; /* [0..7] */
223 
224 /*Level Event Data*/
225 JE_boolean enemyContinualDamage;
226 JE_boolean enemiesActive;
227 JE_boolean forceEvents;
228 JE_boolean stopBackgrounds;
229 JE_byte stopBackgroundNum;
230 JE_byte damageRate;  /*Rate at which a player takes damage*/
231 JE_boolean background3x1;  /*Background 3 enemies use Background 1 X offset*/
232 JE_boolean background3x1b; /*Background 3 enemies moved 8 pixels left*/
233 
234 JE_boolean levelTimer;
235 JE_word    levelTimerCountdown;
236 JE_word    levelTimerJumpTo;
237 JE_boolean randomExplosions;
238 
239 JE_boolean editShip1, editShip2;
240 
241 JE_boolean globalFlags[10]; /* [1..10] */
242 JE_byte levelSong;
243 
244 /* DESTRUCT game */
245 JE_boolean loadDestruct;
246 
247 /* MapView Data */
248 JE_word mapOrigin, mapPNum;
249 JE_byte mapPlanet[5], mapSection[5]; /* [1..5] */
250 
251 /* Interface Constants */
252 JE_boolean moveTyrianLogoUp;
253 JE_boolean skipStarShowVGA;
254 
255 /*EnemyData*/
256 JE_MultiEnemyType enemy;
257 JE_EnemyAvailType enemyAvail;  /* values: 0: used, 1: free, 2: secret pick-up */
258 JE_word enemyOffset;
259 JE_word enemyOnScreen;
260 JE_byte enemyShapeTables[6]; /* [1..6] */
261 JE_word superEnemy254Jump;
262 
263 /*EnemyShotData*/
264 JE_boolean fireButtonHeld;
265 JE_boolean enemyShotAvail[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */
266 EnemyShotType enemyShot[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax]  */
267 
268 /* Player Shot Data */
269 JE_byte     zinglonDuration;
270 JE_byte     astralDuration;
271 JE_word     flareDuration;
272 JE_boolean  flareStart;
273 JE_shortint flareColChg;
274 JE_byte     specialWait;
275 JE_byte     nextSpecialWait;
276 JE_boolean  spraySpecial;
277 JE_byte     doIced;
278 JE_boolean  infiniteShot;
279 
280 /*PlayerData*/
281 JE_boolean allPlayersGone; /*Both players dead and finished exploding*/
282 
283 const uint shadowYDist = 10;
284 
285 JE_real optionSatelliteRotate;
286 
287 JE_integer optionAttachmentMove;
288 JE_boolean optionAttachmentLinked, optionAttachmentReturn;
289 
290 
291 JE_byte chargeWait, chargeLevel, chargeMax, chargeGr, chargeGrWait;
292 
293 JE_word neat;
294 
295 
296 /*ExplosionData*/
297 explosion_type explosions[MAX_EXPLOSIONS]; /* [1..ExplosionMax] */
298 JE_integer explosionFollowAmountX, explosionFollowAmountY;
299 
300 /*Repeating Explosions*/
301 rep_explosion_type rep_explosions[MAX_REPEATING_EXPLOSIONS]; /* [1..20] */
302 
303 /*SuperPixels*/
304 superpixel_type superpixels[MAX_SUPERPIXELS]; /* [0..MaxSP] */
305 unsigned int last_superpixel;
306 
307 /*Temporary Numbers*/
308 JE_byte temp, temp2, temp3;
309 JE_word tempX, tempY;
310 JE_word tempW;
311 
312 JE_boolean doNotSaveBackup;
313 
314 JE_word x, y;
315 JE_integer b;
316 
317 JE_byte **BKwrap1to, **BKwrap2to, **BKwrap3to,
318         **BKwrap1, **BKwrap2, **BKwrap3;
319 
320 JE_shortint specialWeaponFilter, specialWeaponFreq;
321 JE_word     specialWeaponWpn;
322 JE_boolean  linkToPlayer;
323 
324 JE_word shipGr, shipGr2;
325 Sprite2_array *shipGrPtr, *shipGr2ptr;
326 
JE_getShipInfo(void)327 void JE_getShipInfo( void )
328 {
329 	JE_boolean extraShip, extraShip2;
330 
331 	shipGrPtr = &shapes9;
332 	shipGr2ptr = &shapes9;
333 
334 	powerAdd  = powerSys[player[0].items.generator].power;
335 
336 	extraShip = player[0].items.ship > 90;
337 	if (extraShip)
338 	{
339 		JE_byte base = (player[0].items.ship - 91) * 15;
340 		shipGr = JE_SGr(player[0].items.ship - 90, &shipGrPtr);
341 		player[0].armor = extraShips[base + 7];
342 	}
343 	else
344 	{
345 		shipGr = ships[player[0].items.ship].shipgraphic;
346 		player[0].armor = ships[player[0].items.ship].dmg;
347 	}
348 
349 	extraShip2 = player[1].items.ship > 90;
350 	if (extraShip2)
351 	{
352 		JE_byte base2 = (player[1].items.ship - 91) * 15;
353 		shipGr2 = JE_SGr(player[1].items.ship - 90, &shipGr2ptr);
354 		player[1].armor = extraShips[base2 + 7]; /* bug? */
355 	}
356 	else
357 	{
358 		shipGr2 = 0;
359 		player[1].armor = 10;
360 	}
361 
362 	for (uint i = 0; i < COUNTOF(player); ++i)
363 	{
364 		player[i].initial_armor = player[i].armor;
365 
366 
367 		uint temp = ((i == 0 && extraShip) ||
368 		             (i == 1 && extraShip2)) ? 2 : ships[player[i].items.ship].ani;
369 
370 		if (temp == 0)
371 		{
372 			player[i].shot_hit_area_x = 12;
373 			player[i].shot_hit_area_y = 10;
374 		}
375 		else
376 		{
377 			player[i].shot_hit_area_x = 11;
378 			player[i].shot_hit_area_y = 14;
379 		}
380 	}
381 }
382 
JE_SGr(JE_word ship,Sprite2_array ** ptr)383 JE_word JE_SGr( JE_word ship, Sprite2_array **ptr )
384 {
385 	const JE_word GR[15] /* [1..15] */ = {233, 157, 195, 271, 81, 0, 119, 5, 43, 81, 119, 157, 195, 233, 271};
386 
387 	JE_word tempW = extraShips[(ship - 1) * 15];
388 	if (tempW > 7)
389 		*ptr = extraShapes;
390 
391 	return GR[tempW-1];
392 }
393 
JE_drawOptions(void)394 void JE_drawOptions( void )
395 {
396 	SDL_Surface *temp_surface = VGAScreen;
397 	VGAScreen = VGAScreenSeg;
398 
399 	Player *this_player = &player[twoPlayerMode ? 1 : 0];
400 
401 	for (uint i = 0; i < COUNTOF(this_player->sidekick); ++i)
402 	{
403 		JE_OptionType *this_option = &options[this_player->items.sidekick[i]];
404 
405 		this_player->sidekick[i].ammo =
406 		this_player->sidekick[i].ammo_max = this_option->ammo;
407 
408 		this_player->sidekick[i].ammo_refill_ticks =
409 		this_player->sidekick[i].ammo_refill_ticks_max = (105 - this_player->sidekick[i].ammo) * 4;
410 
411 		this_player->sidekick[i].style = this_option->tr;
412 
413 		this_player->sidekick[i].animation_enabled = (this_option->option == 1);
414 		this_player->sidekick[i].animation_frame = 0;
415 
416 		this_player->sidekick[i].charge = 0;
417 		this_player->sidekick[i].charge_ticks = 20;
418 
419 
420 		// draw initial sidekick HUD
421 		const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i];
422 
423 		fill_rectangle_xy(VGAScreenSeg, 284, y, 284 + 28, y + 15, 0);
424 		if (this_option->icongr > 0)
425 			blit_sprite(VGAScreenSeg, 284, y, OPTION_SHAPES, this_option->icongr - 1);  // sidekick HUD icon
426 		draw_segmented_gauge(VGAScreenSeg, 284, y + 13, 112, 2, 2, MAX(1, this_player->sidekick[i].ammo_max / 10), this_player->sidekick[i].ammo);
427 	}
428 
429 	VGAScreen = temp_surface;
430 
431 	JE_drawOptionLevel();
432 }
433 
JE_drawOptionLevel(void)434 void JE_drawOptionLevel( void )
435 {
436 	if (twoPlayerMode)
437 	{
438 		for (temp = 1; temp <= 3; temp++)
439 		{
440 			fill_rectangle_xy(VGAScreenSeg, 268, 127 + (temp - 1) * 6, 269, 127 + 3 + (temp - 1) * 6, 193 + ((player[1].items.sidekick_level - 100) == temp) * 11);
441 		}
442 	}
443 }
444 
JE_tyrianHalt(JE_byte code)445 void JE_tyrianHalt( JE_byte code )
446 {
447 	deinit_audio();
448 	deinit_video();
449 	deinit_joysticks();
450 
451 	/* TODO: NETWORK */
452 
453 	free_main_shape_tables();
454 
455 	free_sprite2s(&shapes6);
456 
457 	for (int i = 0; i < SAMPLE_COUNT; i++)
458 	{
459 		free(digiFx[i]);
460 	}
461 
462 	if (code != 9)
463 	{
464 		/*
465 		TODO?
466 		JE_drawANSI("exitmsg.bin");
467 		JE_gotoXY(1,22);*/
468 
469 		JE_saveConfiguration();
470 	}
471 
472 	/* endkeyboard; */
473 
474 	if (code == 9)
475 	{
476 		/* OutputString('call=file0002.EXE' + #0'); TODO? */
477 	}
478 
479 	if (code == 5)
480 	{
481 		code = 0;
482 	}
483 
484 	if (trentWin)
485 	{
486 		printf("\n"
487 		       "\n"
488 		       "\n"
489 		       "\n"
490 		       "Sleep well, Trent, you deserve the rest.\n"
491 		       "You now have permission to borrow my ship on your next mission.\n"
492 		       "\n"
493 		       "Also, you might want to try out the YESXMAS parameter.\n"
494 		       "  Type: File0001 YESXMAS\n"
495 		       "\n"
496 		       "You'll need the 2.1 patch, though!\n"
497 		       "\n");
498 	}
499 
500 	SDL_Quit();
501 	exit(code);
502 }
503 
JE_specialComplete(JE_byte playerNum,JE_byte specialType)504 void JE_specialComplete( JE_byte playerNum, JE_byte specialType )
505 {
506 	nextSpecialWait = 0;
507 	switch (special[specialType].stype)
508 	{
509 		/*Weapon*/
510 		case 1:
511 			if (playerNum == 1)
512 				b = player_shot_create(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, special[specialType].wpn, playerNum);
513 			else
514 				b = player_shot_create(0, SHOT_SPECIAL2, player[1].x, player[1].y, mouseX, mouseY, special[specialType].wpn, playerNum);
515 
516 			shotRepeat[SHOT_SPECIAL] = shotRepeat[SHOT_SPECIAL2];
517 			break;
518 		/*Repulsor*/
519 		case 2:
520 			for (temp = 0; temp < ENEMY_SHOT_MAX; temp++)
521 			{
522 				if (!enemyShotAvail[temp])
523 				{
524 					if (player[0].x > enemyShot[temp].sx)
525 						enemyShot[temp].sxm--;
526 					else if (player[0].x < enemyShot[temp].sx)
527 						enemyShot[temp].sxm++;
528 
529 					if (player[0].y > enemyShot[temp].sy)
530 						enemyShot[temp].sym--;
531 					else if (player[0].y < enemyShot[temp].sy)
532 						enemyShot[temp].sym++;
533 				}
534 			}
535 			break;
536 		/*Zinglon Blast*/
537 		case 3:
538 			zinglonDuration = 50;
539 			shotRepeat[SHOT_SPECIAL] = 100;
540 			soundQueue[7] = S_SOUL_OF_ZINGLON;
541 			break;
542 		/*Attractor*/
543 		case 4:
544 			for (temp = 0; temp < 100; temp++)
545 			{
546 				if (enemyAvail[temp] != 1 && enemy[temp].scoreitem
547 				    && enemy[temp].evalue != 0)
548 				{
549 					if (player[0].x > enemy[temp].ex)
550 						enemy[temp].exc++;
551 					else if (player[0].x < enemy[temp].ex)
552 						enemy[temp].exc--;
553 
554 					if (player[0].y > enemy[temp].ey)
555 						enemy[temp].eyc++;
556 					else if (player[0].y < enemy[temp].ey)
557 						enemy[temp].eyc--;
558 				}
559 			}
560 			break;
561 		/*Flare*/
562 		case 5:
563 		case 6:
564 		case 7:
565 		case 8:
566 		case 9:
567 		case 10:
568 		case 11:
569 		case 16:
570 			if (flareDuration == 0)
571 				flareStart = true;
572 
573 			specialWeaponWpn = special[specialType].wpn;
574 			linkToPlayer = false;
575 			spraySpecial = false;
576 			switch (special[specialType].stype)
577 			{
578 				case 5:
579 					specialWeaponFilter = 7;
580 					specialWeaponFreq = 2;
581 					flareDuration = 50;
582 					break;
583 				case 6:
584 					specialWeaponFilter = 1;
585 					specialWeaponFreq = 7;
586 					flareDuration = 200 + 25 * player[0].items.weapon[FRONT_WEAPON].power;
587 					break;
588 				case 7:
589 					specialWeaponFilter = 3;
590 					specialWeaponFreq = 3;
591 					flareDuration = 50 + 10 * player[0].items.weapon[FRONT_WEAPON].power;
592 					zinglonDuration = 50;
593 					shotRepeat[SHOT_SPECIAL] = 100;
594 					soundQueue[7] = S_SOUL_OF_ZINGLON;
595 					break;
596 				case 8:
597 					specialWeaponFilter = -99;
598 					specialWeaponFreq = 7;
599 					flareDuration = 10 + player[0].items.weapon[FRONT_WEAPON].power;
600 					break;
601 				case 9:
602 					specialWeaponFilter = -99;
603 					specialWeaponFreq = 8;
604 					flareDuration = 8 + 2 * player[0].items.weapon[FRONT_WEAPON].power;
605 					linkToPlayer = true;
606 					nextSpecialWait = special[specialType].pwr;
607 					break;
608 				case 10:
609 					specialWeaponFilter = -99;
610 					specialWeaponFreq = 8;
611 					flareDuration = 14 + 4 * player[0].items.weapon[FRONT_WEAPON].power;
612 					linkToPlayer = true;
613 					break;
614 				case 11:
615 					specialWeaponFilter = -99;
616 					specialWeaponFreq = special[specialType].pwr;
617 					flareDuration = 10 + 10 * player[0].items.weapon[FRONT_WEAPON].power;
618 					astralDuration = 20 + 10 * player[0].items.weapon[FRONT_WEAPON].power;
619 					break;
620 				case 16:
621 					specialWeaponFilter = -99;
622 					specialWeaponFreq = 8;
623 					flareDuration = temp2 * 16 + 8;
624 					linkToPlayer = true;
625 					spraySpecial = true;
626 					break;
627 			}
628 			break;
629 		case 12:
630 			player[playerNum-1].invulnerable_ticks = temp2 * 10;
631 
632 			if (superArcadeMode > 0 && superArcadeMode <= SA)
633 			{
634 				shotRepeat[SHOT_SPECIAL] = 250;
635 				b = player_shot_create(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, 707, 1);
636 				player[0].invulnerable_ticks = 100;
637 			}
638 			break;
639 		case 13:
640 			player[0].armor += temp2 / 4 + 1;
641 
642 			soundQueue[3] = S_POWERUP;
643 			break;
644 		case 14:
645 			player[1].armor += temp2 / 4 + 1;
646 
647 			soundQueue[3] = S_POWERUP;
648 			break;
649 
650 		case 17:  // spawn left or right sidekick
651 			soundQueue[3] = S_POWERUP;
652 
653 			if (player[0].items.sidekick[LEFT_SIDEKICK] == special[specialType].wpn)
654 			{
655 				player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn;
656 				shotMultiPos[RIGHT_SIDEKICK] = 0;
657 			}
658 			else
659 			{
660 				player[0].items.sidekick[LEFT_SIDEKICK] = special[specialType].wpn;
661 				shotMultiPos[LEFT_SIDEKICK] = 0;
662 			}
663 
664 			JE_drawOptions();
665 			break;
666 
667 		case 18:  // spawn right sidekick
668 			player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn;
669 
670 			JE_drawOptions();
671 
672 			soundQueue[4] = S_POWERUP;
673 
674 			shotMultiPos[RIGHT_SIDEKICK] = 0;
675 			break;
676 	}
677 }
678 
JE_doSpecialShot(JE_byte playerNum,uint * armor,uint * shield)679 void JE_doSpecialShot( JE_byte playerNum, uint *armor, uint *shield )
680 {
681 	if (player[0].items.special > 0)
682 	{
683 		if (shotRepeat[SHOT_SPECIAL] == 0 && specialWait == 0 && flareDuration < 2 && zinglonDuration < 2)
684 			blit_sprite2(VGAScreen, 47, 4, shapes9, 94);
685 		else
686 			blit_sprite2(VGAScreen, 47, 4, shapes9, 93);
687 	}
688 
689 	if (shotRepeat[SHOT_SPECIAL] > 0)
690 	{
691 		--shotRepeat[SHOT_SPECIAL];
692 	}
693 	if (specialWait > 0)
694 	{
695 		specialWait--;
696 	}
697 	temp = SFExecuted[playerNum-1];
698 	if (temp > 0 && shotRepeat[SHOT_SPECIAL] == 0 && flareDuration == 0)
699 	{
700 		temp2 = special[temp].pwr;
701 
702 		bool can_afford = true;
703 
704 		if (temp2 > 0)
705 		{
706 			if (temp2 < 98)  // costs some shield
707 			{
708 				if (*shield >= temp2)
709 					*shield -= temp2;
710 				else
711 					can_afford = false;
712 			}
713 			else if (temp2 == 98)  // costs all shield
714 			{
715 				if (*shield < 4)
716 					can_afford = false;
717 				temp2 = *shield;
718 				*shield = 0;
719 			}
720 			else if (temp2 == 99)  // costs half shield
721 			{
722 				temp2 = *shield / 2;
723 				*shield = temp2;
724 			}
725 			else  // costs some armor
726 			{
727 				temp2 -= 100;
728 				if (*armor > temp2)
729 					*armor -= temp2;
730 				else
731 					can_afford = false;
732 			}
733 		}
734 
735 		shotMultiPos[SHOT_SPECIAL] = 0;
736 		shotMultiPos[SHOT_SPECIAL2] = 0;
737 
738 		if (can_afford)
739 			JE_specialComplete(playerNum, temp);
740 
741 		SFExecuted[playerNum-1] = 0;
742 
743 		JE_wipeShieldArmorBars();
744 		VGAScreen = VGAScreenSeg; /* side-effect of game_screen */
745 		JE_drawShield();
746 		JE_drawArmor();
747 		VGAScreen = game_screen; /* side-effect of game_screen */
748 	}
749 
750 	if (playerNum == 1 && player[0].items.special > 0)
751 	{  /*Main Begin*/
752 
753 		if (superArcadeMode > 0 && (button[2-1] || button[3-1]))
754 		{
755 			fireButtonHeld = false;
756 		}
757 		if (!button[1-1] && !(superArcadeMode != SA_NONE && (button[2-1] || button[3-1])))
758 		{
759 			fireButtonHeld = false;
760 		}
761 		else if (shotRepeat[SHOT_SPECIAL] == 0 && !fireButtonHeld && !(flareDuration > 0) && specialWait == 0)
762 		{
763 			fireButtonHeld = true;
764 			JE_specialComplete(playerNum, player[0].items.special);
765 		}
766 
767 	}  /*Main End*/
768 
769 	if (astralDuration > 0)
770 		astralDuration--;
771 
772 	shotAvail[MAX_PWEAPON-1] = 0;
773 	if (flareDuration > 1)
774 	{
775 		if (specialWeaponFilter != -99)
776 		{
777 			if (levelFilter == -99 && levelBrightness == -99)
778 			{
779 				filterActive = false;
780 			}
781 			if (!filterActive)
782 			{
783 				levelFilter = specialWeaponFilter;
784 				if (levelFilter == 7)
785 				{
786 					levelBrightness = 0;
787 				}
788 				filterActive = true;
789 			}
790 
791 			if (mt_rand() % 2 == 0)
792 				flareColChg = -1;
793 			else
794 				flareColChg = 1;
795 
796 			if (levelFilter == 7)
797 			{
798 				if (levelBrightness < -6)
799 				{
800 					flareColChg = 1;
801 				}
802 				if (levelBrightness > 6)
803 				{
804 					flareColChg = -1;
805 				}
806 				levelBrightness += flareColChg;
807 			}
808 		}
809 
810 		if ((signed)(mt_rand() % 6) < specialWeaponFreq)
811 		{
812 			b = MAX_PWEAPON;
813 
814 			if (linkToPlayer)
815 			{
816 				if (shotRepeat[SHOT_SPECIAL] == 0)
817 				{
818 					b = player_shot_create(0, SHOT_SPECIAL, player[0].x, player[0].y, mouseX, mouseY, specialWeaponWpn, playerNum);
819 				}
820 			}
821 			else
822 			{
823 				b = player_shot_create(0, SHOT_SPECIAL, mt_rand() % 280, mt_rand() % 180, mouseX, mouseY, specialWeaponWpn, playerNum);
824 			}
825 
826 			if (spraySpecial && b != MAX_PWEAPON)
827 			{
828 				playerShotData[b].shotXM = (mt_rand() % 5) - 2;
829 				playerShotData[b].shotYM = (mt_rand() % 5) - 2;
830 				if (playerShotData[b].shotYM == 0)
831 				{
832 					playerShotData[b].shotYM++;
833 				}
834 			}
835 		}
836 
837 		flareDuration--;
838 		if (flareDuration == 1)
839 		{
840 			specialWait = nextSpecialWait;
841 		}
842 	}
843 	else if (flareStart)
844 	{
845 		flareStart = false;
846 		shotRepeat[SHOT_SPECIAL] = linkToPlayer ? 15 : 200;
847 		flareDuration = 0;
848 		if (levelFilter == specialWeaponFilter)
849 		{
850 			levelFilter = -99;
851 			levelBrightness = -99;
852 			filterActive = false;
853 		}
854 	}
855 
856 	if (zinglonDuration > 1)
857 	{
858 		temp = 25 - abs(zinglonDuration - 25);
859 
860 		JE_barBright(VGAScreen, player[0].x + 7 - temp,     0, player[0].x + 7 + temp,     184);
861 		JE_barBright(VGAScreen, player[0].x + 7 - temp - 2, 0, player[0].x + 7 + temp + 2, 184);
862 
863 		zinglonDuration--;
864 		if (zinglonDuration % 5 == 0)
865 		{
866 			shotAvail[MAX_PWEAPON-1] = 1;
867 		}
868 	}
869 }
870 
JE_setupExplosion(signed int x,signed int y,signed int delta_y,unsigned int type,bool fixed_position,bool follow_player)871 void JE_setupExplosion( signed int x, signed int y, signed int delta_y, unsigned int type, bool fixed_position, bool follow_player )
872 {
873 	const struct {
874 		JE_word sprite;
875 		JE_byte ttl;
876 	} explosion_data[53] /* [1..53] */ = {
877 		{ 144,  7 },
878 		{ 120, 12 },
879 		{ 190, 12 },
880 		{ 209, 12 },
881 		{ 152, 12 },
882 		{ 171, 12 },
883 		{ 133,  7 },   /*White Smoke*/
884 		{   1, 12 },
885 		{  20, 12 },
886 		{  39, 12 },
887 		{  58, 12 },
888 		{ 110,  3 },
889 		{  76,  7 },
890 		{  91,  3 },
891 /*15*/	{ 227,  3 },
892 		{ 230,  3 },
893 		{ 233,  3 },
894 		{ 252,  3 },
895 		{ 246,  3 },
896 /*20*/	{ 249,  3 },
897 		{ 265,  3 },
898 		{ 268,  3 },
899 		{ 271,  3 },
900 		{ 236,  3 },
901 /*25*/	{ 239,  3 },
902 		{ 242,  3 },
903 		{ 261,  3 },
904 		{ 274,  3 },
905 		{ 277,  3 },
906 /*30*/	{ 280,  3 },
907 		{ 299,  3 },
908 		{ 284,  3 },
909 		{ 287,  3 },
910 		{ 290,  3 },
911 /*35*/	{ 293,  3 },
912 		{ 165,  8 },   /*Coin Values*/
913 		{ 184,  8 },
914 		{ 203,  8 },
915 		{ 222,  8 },
916 		{ 168,  8 },
917 		{ 187,  8 },
918 		{ 206,  8 },
919 		{ 225, 10 },
920 		{ 169, 10 },
921 		{ 188, 10 },
922 		{ 207, 20 },
923 		{ 226, 14 },
924 		{ 170, 14 },
925 		{ 189, 14 },
926 		{ 208, 14 },
927 		{ 246, 14 },
928 		{ 227, 14 },
929 		{ 265, 14 }
930 	};
931 
932 	if (y > -16 && y < 190)
933 	{
934 		for (int i = 0; i < MAX_EXPLOSIONS; i++)
935 		{
936 			if (explosions[i].ttl == 0)
937 			{
938 				explosions[i].x = x;
939 				explosions[i].y = y;
940 				if (type == 6)
941 				{
942 					explosions[i].y += 12;
943 					explosions[i].x += 2;
944 				} else if (type == 98)
945 				{
946 					type = 6;
947 				}
948 				explosions[i].sprite = explosion_data[type].sprite;
949 				explosions[i].ttl = explosion_data[type].ttl;
950 				explosions[i].follow_player = follow_player;
951 				explosions[i].fixed_position = fixed_position;
952 				explosions[i].delta_x = 0;
953 				explosions[i].delta_y = delta_y;
954 				break;
955 			}
956 		}
957 	}
958 }
959 
JE_setupExplosionLarge(JE_boolean enemyGround,JE_byte exploNum,JE_integer x,JE_integer y)960 void JE_setupExplosionLarge( JE_boolean enemyGround, JE_byte exploNum, JE_integer x, JE_integer y )
961 {
962 	if (y >= 0)
963 	{
964 		if (enemyGround)
965 		{
966 			JE_setupExplosion(x - 6, y - 14, 0,  2, false, false);
967 			JE_setupExplosion(x + 6, y - 14, 0,  4, false, false);
968 			JE_setupExplosion(x - 6, y,      0,  3, false, false);
969 			JE_setupExplosion(x + 6, y,      0,  5, false, false);
970 		} else {
971 			JE_setupExplosion(x - 6, y - 14, 0,  7, false, false);
972 			JE_setupExplosion(x + 6, y - 14, 0,  9, false, false);
973 			JE_setupExplosion(x - 6, y,      0,  8, false, false);
974 			JE_setupExplosion(x + 6, y,      0, 10, false, false);
975 		}
976 
977 		bool big;
978 
979 		if (exploNum > 10)
980 		{
981 			exploNum -= 10;
982 			big = true;
983 		}
984 		else
985 		{
986 			big = false;
987 		}
988 
989 		if (exploNum)
990 		{
991 			for (int i = 0; i < MAX_REPEATING_EXPLOSIONS; i++)
992 			{
993 				if (rep_explosions[i].ttl == 0)
994 				{
995 					rep_explosions[i].ttl = exploNum;
996 					rep_explosions[i].delay = 2;
997 					rep_explosions[i].x = x;
998 					rep_explosions[i].y = y;
999 					rep_explosions[i].big = big;
1000 					break;
1001 				}
1002 			}
1003 		}
1004 	}
1005 }
1006 
JE_wipeShieldArmorBars(void)1007 void JE_wipeShieldArmorBars( void )
1008 {
1009 	if (!twoPlayerMode || galagaMode)
1010 	{
1011 		fill_rectangle_xy(VGAScreenSeg, 270, 137, 278, 194 - player[0].shield * 2, 0);
1012 	}
1013 	else
1014 	{
1015 		fill_rectangle_xy(VGAScreenSeg, 270, 60 - 44, 278, 60, 0);
1016 		fill_rectangle_xy(VGAScreenSeg, 270, 194 - 44, 278, 194, 0);
1017 	}
1018 	if (!twoPlayerMode || galagaMode)
1019 	{
1020 		fill_rectangle_xy(VGAScreenSeg, 307, 137, 315, 194 - player[0].armor * 2, 0);
1021 	}
1022 	else
1023 	{
1024 		fill_rectangle_xy(VGAScreenSeg, 307, 60 - 44, 315, 60, 0);
1025 		fill_rectangle_xy(VGAScreenSeg, 307, 194 - 44, 315, 194, 0);
1026 	}
1027 }
1028 
JE_playerDamage(JE_byte temp,Player * this_player)1029 JE_byte JE_playerDamage( JE_byte temp,
1030                          Player *this_player )
1031 {
1032 	int playerDamage = 0;
1033 	soundQueue[7] = S_SHIELD_HIT;
1034 
1035 	/* Player Damage Routines */
1036 	if (this_player->shield < temp)
1037 	{
1038 		playerDamage = temp;
1039 		temp -= this_player->shield;
1040 		this_player->shield = 0;
1041 
1042 		if (temp > 0)
1043 		{
1044 			/*Through Shields - Now Armor */
1045 
1046 			if (this_player->armor < temp)
1047 			{
1048 				temp -= this_player->armor;
1049 				this_player->armor = 0;
1050 
1051 				if (this_player->is_alive && !youAreCheating)
1052 				{
1053 					levelTimer = false;
1054 					this_player->is_alive = false;
1055 					this_player->exploding_ticks = 60;
1056 					levelEnd = 40;
1057 					tempVolume = tyrMusicVolume;
1058 					soundQueue[1] = S_EXPLOSION_22;
1059 				}
1060 			}
1061 			else
1062 			{
1063 				this_player->armor -= temp;
1064 				soundQueue[7] = S_HULL_HIT;
1065 			}
1066 		}
1067 	}
1068 	else
1069 	{
1070 		this_player->shield -= temp;
1071 
1072 		JE_setupExplosion(this_player->x - 17, this_player->y - 12, 0, 14, false, !twoPlayerMode);
1073 		JE_setupExplosion(this_player->x - 5 , this_player->y - 12, 0, 15, false, !twoPlayerMode);
1074 		JE_setupExplosion(this_player->x + 7 , this_player->y - 12, 0, 16, false, !twoPlayerMode);
1075 		JE_setupExplosion(this_player->x + 19, this_player->y - 12, 0, 17, false, !twoPlayerMode);
1076 
1077 		JE_setupExplosion(this_player->x - 17, this_player->y + 2, 0,  18, false, !twoPlayerMode);
1078 		JE_setupExplosion(this_player->x + 19, this_player->y + 2, 0,  19, false, !twoPlayerMode);
1079 
1080 		JE_setupExplosion(this_player->x - 17, this_player->y + 16, 0, 20, false, !twoPlayerMode);
1081 		JE_setupExplosion(this_player->x - 5 , this_player->y + 16, 0, 21, false, !twoPlayerMode);
1082 		JE_setupExplosion(this_player->x + 7 , this_player->y + 16, 0, 22, false, !twoPlayerMode);
1083 	}
1084 
1085 	JE_wipeShieldArmorBars();
1086 	VGAScreen = VGAScreenSeg; /* side-effect of game_screen */
1087 	JE_drawShield();
1088 	JE_drawArmor();
1089 	VGAScreen = game_screen; /* side-effect of game_screen */
1090 
1091 	return playerDamage;
1092 }
1093 
JE_portConfigs(void)1094 JE_word JE_portConfigs( void )
1095 {
1096 	const uint player_index = twoPlayerMode ? 1 : 0;
1097 	return tempW = weaponPort[player[player_index].items.weapon[REAR_WEAPON].id].opnum;
1098 }
1099 
JE_drawShield(void)1100 void JE_drawShield( void )
1101 {
1102 	if (twoPlayerMode && !galagaMode)
1103 	{
1104 		for (uint i = 0; i < COUNTOF(player); ++i)
1105 			JE_dBar3(VGAScreen, 270, 60 + 134 * i, roundf(player[i].shield * 0.8f), 144);
1106 	}
1107 	else
1108 	{
1109 		JE_dBar3(VGAScreen, 270, 194, player[0].shield, 144);
1110 		if (player[0].shield != player[0].shield_max)
1111 		{
1112 			const uint y = 193 - (player[0].shield_max * 2);
1113 			JE_rectangle(VGAScreen, 270, y, 278, y, 68); /* <MXD> SEGa000 */
1114 		}
1115 	}
1116 }
1117 
JE_drawArmor(void)1118 void JE_drawArmor( void )
1119 {
1120 	for (uint i = 0; i < COUNTOF(player); ++i)
1121 		if (player[i].armor > 28)
1122 			player[i].armor = 28;
1123 
1124 	if (twoPlayerMode && !galagaMode)
1125 	{
1126 		for (uint i = 0; i < COUNTOF(player); ++i)
1127 			JE_dBar3(VGAScreen, 307, 60 + 134 * i, roundf(player[i].armor * 0.8f), 224);
1128 	}
1129 	else
1130 	{
1131 		JE_dBar3(VGAScreen, 307, 194, player[0].armor, 224);
1132 	}
1133 }
1134 
JE_doSP(JE_word x,JE_word y,JE_word num,JE_byte explowidth,JE_byte color)1135 void JE_doSP( JE_word x, JE_word y, JE_word num, JE_byte explowidth, JE_byte color ) /* superpixels */
1136 {
1137 	for (temp = 0; temp < num; temp++)
1138 	{
1139 		JE_real tempr = mt_rand_lt1() * (2 * M_PI);
1140 		signed int tempy = roundf(cosf(tempr) * mt_rand_1() * explowidth);
1141 		signed int tempx = roundf(sinf(tempr) * mt_rand_1() * explowidth);
1142 
1143 		if (++last_superpixel >= MAX_SUPERPIXELS)
1144 			last_superpixel = 0;
1145 		superpixels[last_superpixel].x = tempx + x;
1146 		superpixels[last_superpixel].y = tempy + y;
1147 		superpixels[last_superpixel].delta_x = tempx;
1148 		superpixels[last_superpixel].delta_y = tempy + 1;
1149 		superpixels[last_superpixel].color = color;
1150 		superpixels[last_superpixel].z = 15;
1151 	}
1152 }
1153 
JE_drawSP(void)1154 void JE_drawSP( void )
1155 {
1156 	for (int i = MAX_SUPERPIXELS; i--; )
1157 	{
1158 		if (superpixels[i].z)
1159 		{
1160 			superpixels[i].x += superpixels[i].delta_x;
1161 			superpixels[i].y += superpixels[i].delta_y;
1162 
1163 			if (superpixels[i].x < (unsigned)VGAScreen->w && superpixels[i].y < (unsigned)VGAScreen->h)
1164 			{
1165 				Uint8 *s = (Uint8 *)VGAScreen->pixels; /* screen pointer, 8-bit specific */
1166 				s += superpixels[i].y * VGAScreen->pitch;
1167 				s += superpixels[i].x;
1168 
1169 				*s = (((*s & 0x0f) + superpixels[i].z) >> 1) + superpixels[i].color;
1170 				if (superpixels[i].x > 0)
1171 					*(s - 1) = (((*(s - 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color;
1172 				if (superpixels[i].x < VGAScreen->w - 1u)
1173 					*(s + 1) = (((*(s + 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color;
1174 				if (superpixels[i].y > 0)
1175 					*(s - VGAScreen->pitch) = (((*(s - VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color;
1176 				if (superpixels[i].y < VGAScreen->h - 1u)
1177 					*(s + VGAScreen->pitch) = (((*(s + VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color;
1178 			}
1179 
1180 			superpixels[i].z--;
1181 		}
1182 	}
1183 }
1184 
1185