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