/* * OpenTyrian Classic: A modern cross-platform port of Tyrian * Copyright (C) 2007-2009 The OpenTyrian Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "editship.h" #include "episodes.h" #include "joystick.h" #include "lds_play.h" #include "loudness.h" #include "mainint.h" #include "mouse.h" #include "mtrand.h" #include "network.h" #include "nortsong.h" #include "nortvars.h" #include "opentyr.h" #include "sprite.h" #include "varz.h" #include "vga256d.h" #include "video.h" JE_integer tempDat, tempDat2, tempDat3; 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 const JE_word SASpecialWeapon[SA] /* [1..SA] */ = { 7, 8, 9, 10, 11, 12, 13 }; const JE_word SASpecialWeaponB[SA] /* [1..SA] */ = {37, 6, 15, 40, 16, 14, 41 }; const JE_byte SAShip[SA] /* [1..SA] */ = { 3, 1, 5, 10, 2, 11, 12 }; const JE_word SAWeapon[SA][5] /* [1..SA, 1..5] */ = { /* R Bl Bk G P */ { 9, 31, 32, 33, 34 }, /* Stealth Ship */ { 19, 8, 22, 41, 34 }, /* StormWind */ { 27, 5, 20, 42, 31 }, /* Techno */ { 15, 3, 28, 22, 12 }, /* Enemy */ { 23, 35, 25, 14, 6 }, /* Weird */ { 2, 5, 21, 4, 7 }, /* Unknown */ { 40, 38, 37, 41, 36 } /* NortShip Z */ }; const JE_byte specialArcadeWeapon[PORT_NUM] /* [1..Portnum] */ = { 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, -0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0 }; const JE_byte optionSelect[16][3][2] /* [0..15, 1..3, 1..2] */ = { /* MAIN OPT FRONT */ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 1, 1},{16,16},{30,30} }, /*Single Shot*/ { { 2, 2},{29,29},{29,20} }, /*Dual Shot*/ { { 3, 3},{21,21},{12, 0} }, /*Charge Cannon*/ { { 4, 4},{18,18},{16,23} }, /*Vulcan*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 6, 6},{29,16},{ 0,22} }, /*Super Missile*/ { { 7, 7},{19,19},{19,28} }, /*Atom Bomb*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { {10,10},{21,21},{21,27} }, /*Mini Missile*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { {13,13},{17,17},{13,26} }, /*MicroBomb*/ { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ { {15,15},{15,16},{15,16} } /*Post-It*/ }; const JE_word PGR[21] /* [1..21] */ = { 4, 1,2,3, 41-21,57-21,73-21,89-21,105-21, 121-21,137-21,153-21, 151,151,151,151,73-21,73-21,1,2,4 /*151,151,151*/ }; 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}; const JE_word linkGunWeapons[38] /* [1..38] */ = { 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, 445,446,447,448,449,445,446,447,448,449,450,451 }; const JE_word chargeGunWeapons[38] /* [1..38] */ = { 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, 458,458,458,458,458,458,458,458,458,458,458,458 }; const JE_word linkMultiGr[17] /* [0..16] */ = {77,221,183,301,1,282,164,202,58,201,163,281,39,300,182,220,77}; const JE_word linkSonicGr[17] /* [0..16] */ = {85,242,131,303,47,284,150,223,66,224,149,283,9,302,130,243,85}; const JE_word linkMult2Gr[17] /* [0..16] */ = {78,299,295,297,2,278,276,280,59,279,275,277,40,296,294,298,78}; const JE_byte randomEnemyLaunchSounds[3] /* [1..3] */ = {13,6,26}; /* YKS: Twiddle cheat sheet: * 1: UP * 2: DOWN * 3: LEFT * 4: RIGHT * 5: UP+FIRE * 6: DOWN+FIRE * 7: LEFT+FIRE * 8: RIGHT+FIRE * 9: Release all keys (directions and fire) */ const JE_byte keyboardCombos[26][8] /* [1..26, 1..8] */ = { { 2, 1, 2, 5, 137, 0, 0, 0}, /*Invulnerability*/ { 4, 3, 2, 5, 138, 0, 0, 0}, /*Atom Bomb*/ { 3, 4, 6, 139, 0, 0, 0, 0}, /*Seeker Bombs*/ { 2, 5, 142, 0, 0, 0, 0, 0}, /*Ice Blast*/ { 6, 2, 6, 143, 0, 0, 0, 0}, /*Auto Repair*/ { 6, 7, 5, 8, 6, 7, 5, 112 }, /*Spin Wave*/ { 7, 8, 101, 0, 0, 0, 0, 0}, /*Repulsor*/ { 1, 7, 6, 146, 0, 0, 0, 0}, /*Protron Field*/ { 8, 6, 7, 1, 120, 0, 0, 0}, /*Minefield*/ { 3, 6, 8, 5, 121, 0, 0, 0}, /*Post-It Blast*/ { 1, 2, 7, 8, 119, 0, 0, 0}, /*Drone Ship - TBC*/ { 3, 4, 3, 6, 123, 0, 0, 0}, /*Repair Player 2*/ { 6, 7, 5, 8, 124, 0, 0, 0}, /*Super Bomb - TBC*/ { 1, 6, 125, 0, 0, 0, 0, 0}, /*Hot Dog*/ { 9, 5, 126, 0, 0, 0, 0, 0}, /*Lightning UP */ { 1, 7, 127, 0, 0, 0, 0, 0}, /*Lightning UP+LEFT */ { 1, 8, 128, 0, 0, 0, 0, 0}, /*Lightning UP+RIGHT*/ { 9, 7, 129, 0, 0, 0, 0, 0}, /*Lightning LEFT */ { 9, 8, 130, 0, 0, 0, 0, 0}, /*Lightning RIGHT*/ { 4, 2, 3, 5, 131, 0, 0, 0}, /*Warfly */ { 3, 1, 2, 8, 132, 0, 0, 0}, /*FrontBlaster */ { 2, 4, 5, 133, 0, 0, 0, 0}, /*Gerund */ { 3, 4, 2, 8, 134, 0, 0, 0}, /*FireBomb */ { 1, 4, 6, 135, 0, 0, 0, 0}, /*Indigo */ { 1, 3, 6, 137, 0, 0, 0, 0}, /*Invulnerability [easier] */ { 1, 4, 3, 4, 7, 136, 0, 0} /*D-Media Protron Drone */ }; const JE_byte shipCombosB[21] /* [1..21] */ = {15,16,17,18,19,20,21,22,23,24, 7, 8, 5,25,14, 4, 6, 3, 9, 2,26}; /*!! SUPER Tyrian !!*/ const JE_byte superTyrianSpecials[4] /* [1..4] */ = {1,2,4,5}; const JE_byte shipCombos[14][3] /* [0..12, 1..3] */ = { { 5, 4, 7}, /*2nd Player ship*/ { 1, 2, 0}, /*USP Talon*/ {14, 4, 0}, /*Super Carrot*/ { 4, 5, 0}, /*Gencore Phoenix*/ { 6, 5, 0}, /*Gencore Maelstrom*/ { 7, 8, 0}, /*MicroCorp Stalker*/ { 7, 9, 0}, /*MicroCorp Stalker-B*/ {10, 3, 5}, /*Prototype Stalker-C*/ { 5, 8, 9}, /*Stalker*/ { 1, 3, 0}, /*USP Fang*/ { 7,16,17}, /*U-Ship*/ { 2,11,12}, /*1st Player ship*/ { 3, 8,10}, /*Nort ship*/ { 0, 0, 0} // Dummy entry added for Stalker 21.126 }; /*Street-Fighter Commands*/ JE_byte SFCurrentCode[2][21]; /* [1..2, 1..21] */ JE_byte SFExecuted[2]; /* [1..2] */ /*Special General Data*/ JE_byte lvlFileNum; JE_word maxEvent, eventLoc; /*JE_word maxenemies;*/ JE_word tempBackMove, explodeMove; /*Speed of background movement*/ JE_byte levelEnd; JE_word levelEndFxWait; JE_shortint levelEndWarp; JE_boolean endLevel, reallyEndLevel, waitToEndLevel, playerEndLevel, normalBonusLevelCurrent, bonusLevelCurrent, smallEnemyAdjust, readyToEndLevel, quitRequested; JE_byte newPL[10]; /* [0..9] */ /*Eventsys event 75 parameter*/ JE_word returnLoc; JE_boolean returnActive; JE_word galagaShotFreq; JE_longint galagaLife; JE_boolean debug = false; /*Debug Mode*/ Uint32 debugTime, lastDebugTime; JE_longint debugHistCount; JE_real debugHist; JE_word curLoc; /*Current Pixel location of background 1*/ JE_boolean firstGameOver, gameLoaded, enemyStillExploding; /* Destruction Ratio */ JE_word totalEnemy; JE_word enemyKilled; /* Shape/Map Data - All in one Segment! */ struct JE_MegaDataType1 megaData1; struct JE_MegaDataType2 megaData2; struct JE_MegaDataType3 megaData3; /* Secret Level Display */ JE_byte flash; JE_shortint flashChange; JE_byte displayTime; /* Demo Stuff */ bool play_demo = false, record_demo = false, stopped_demo = false; Uint8 demo_num = 0; FILE *demo_file = NULL; Uint8 demo_keys, next_demo_keys; Uint16 demo_keys_wait; /* Sound Effects Queue */ JE_byte soundQueue[8]; /* [0..7] */ /*Level Event Data*/ JE_boolean enemyContinualDamage; JE_boolean enemiesActive; JE_boolean forceEvents; JE_boolean stopBackgrounds; JE_byte stopBackgroundNum; JE_byte damageRate; /*Rate at which a player takes damage*/ JE_boolean background3x1; /*Background 3 enemies use Background 1 X offset*/ JE_boolean background3x1b; /*Background 3 enemies moved 8 pixels left*/ JE_boolean levelTimer; JE_word levelTimerCountdown; JE_word levelTimerJumpTo; JE_boolean randomExplosions; JE_boolean editShip1, editShip2; JE_boolean globalFlags[10]; /* [1..10] */ JE_byte levelSong; /* DESTRUCT game */ JE_boolean loadDestruct; /* MapView Data */ JE_word mapOrigin, mapPNum; JE_byte mapPlanet[5], mapSection[5]; /* [1..5] */ /* Interface Constants */ JE_boolean moveTyrianLogoUp; JE_boolean skipStarShowVGA; /*EnemyData*/ JE_EnemyType enemy; JE_EnemyAvailType enemyAvail; JE_word enemyOffset; JE_word enemyOnScreen; JE_byte enemyShapeTables[6]; /* [1..6] */ JE_boolean uniqueEnemy; JE_word superEnemy254Jump; /*EnemyShotData*/ JE_boolean fireButtonHeld; JE_boolean enemyShotAvail[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */ EnemyShotType enemyShot[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */ /* Player Shot Data */ JE_byte zinglonDuration; JE_byte astralDuration; JE_word flareDuration; JE_boolean flareStart; JE_shortint flareColChg; JE_byte specialWait; JE_byte nextSpecialWait; JE_boolean spraySpecial; JE_byte doIced; JE_boolean infiniteShot; PlayerShotDataType playerShotData[MAX_PWEAPON + 1]; /* [1..MaxPWeapon+1] */ JE_byte chain; /*PlayerData*/ JE_boolean allPlayersGone; /*Both players dead and finished exploding*/ JE_byte shotAvail[MAX_PWEAPON]; /* [1..MaxPWeapon] */ /*0:Avail 1-255:Duration left*/ const uint shadowYDist = 10; JE_real optionSatelliteRotate; JE_integer optionAttachmentMove; JE_boolean optionAttachmentLinked, optionAttachmentReturn; JE_byte chargeWait, chargeLevel, chargeMax, chargeGr, chargeGrWait; JE_word neat; /*ExplosionData*/ explosion_type explosions[MAX_EXPLOSIONS]; /* [1..ExplosionMax] */ JE_integer explosionFollowAmountX, explosionFollowAmountY; /*Repeating Explosions*/ rep_explosion_type rep_explosions[MAX_REPEATING_EXPLOSIONS]; /* [1..20] */ /*SuperPixels*/ superpixel_type superpixels[MAX_SUPERPIXELS]; /* [0..MaxSP] */ unsigned int last_superpixel; /*Temporary Numbers*/ JE_integer tempI, tempI2, tempI3, tempI4; JE_longint tempL; JE_byte temp, temp2, temp3, temp4, temp5, tempPos; JE_word tempX, tempY, tempX2, tempY2; JE_word tempW, tempW2; JE_boolean doNotSaveBackup; JE_word x, y; JE_integer b; JE_byte playerNum; JE_byte **BKwrap1to, **BKwrap2to, **BKwrap3to, **BKwrap1, **BKwrap2, **BKwrap3; JE_shortint specialWeaponFilter, specialWeaponFreq; JE_word specialWeaponWpn; JE_boolean linkToPlayer; JE_word shipGr, shipGr2; Sprite2_array *shipGrPtr, *shipGr2ptr; void JE_getShipInfo( void ) { JE_boolean extraShip, extraShip2; shipGrPtr = &shapes9; shipGr2ptr = &shapes9; powerAdd = powerSys[player[0].items.generator].power; extraShip = player[0].items.ship > 90; if (extraShip) { JE_byte base = (player[0].items.ship - 91) * 15; shipGr = JE_SGr(player[0].items.ship - 90, &shipGrPtr); player[0].armor = extraShips[base + 7]; } else { shipGr = ships[player[0].items.ship].shipgraphic; player[0].armor = ships[player[0].items.ship].dmg; } extraShip2 = player[1].items.ship > 90; if (extraShip2) { JE_byte base2 = (player[1].items.ship - 91) * 15; shipGr2 = JE_SGr(player[1].items.ship - 90, &shipGr2ptr); player[1].armor = extraShips[base2 + 7]; /* bug? */ } else { shipGr2 = 0; player[1].armor = 10; } for (uint i = 0; i < COUNTOF(player); ++i) { player[i].initial_armor = player[i].armor; uint temp = ((i == 0 && extraShip) || (i == 1 && extraShip2)) ? 2 : ships[player[i].items.ship].ani; if (temp == 0) { player[i].shot_hit_area_x = 12; player[i].shot_hit_area_y = 10; } else { player[i].shot_hit_area_x = 11; player[i].shot_hit_area_y = 14; } } } JE_word JE_SGr( JE_word ship, Sprite2_array **ptr ) { const JE_word GR[15] /* [1..15] */ = {233, 157, 195, 271, 81, 0, 119, 5, 43, 81, 119, 157, 195, 233, 271}; JE_word tempW = extraShips[(ship - 1) * 15]; if (tempW > 7) *ptr = (Sprite2_array*)extraShapes; return GR[tempW-1]; } void JE_drawOptions( void ) { SDL_Surface *temp_surface = VGAScreen; VGAScreen = VGAScreenSeg; Player *this_player = &player[twoPlayerMode ? 1 : 0]; for (uint i = 0; i < COUNTOF(this_player->sidekick); ++i) { JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; this_player->sidekick[i].ammo = this_player->sidekick[i].ammo_max = this_option->ammo; this_player->sidekick[i].ammo_refill_ticks = this_player->sidekick[i].ammo_refill_ticks_max = (105 - this_player->sidekick[i].ammo) * 4; this_player->sidekick[i].style = this_option->tr; this_player->sidekick[i].animation_enabled = (this_option->option == 1); this_player->sidekick[i].animation_frame = 0; this_player->sidekick[i].charge = 0; this_player->sidekick[i].charge_ticks = 20; // draw initial sidekick HUD const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i]; fill_rectangle_xy(VGAScreenSeg, 284, y, 284 + 28, y + 15, 0); if (this_option->icongr > 0) blit_sprite(VGAScreenSeg, 284, y, OPTION_SHAPES, this_option->icongr - 1); // sidekick HUD icon draw_segmented_gauge(VGAScreenSeg, 284, y + 13, 112, 2, 2, MAX(1, this_player->sidekick[i].ammo_max / 10), this_player->sidekick[i].ammo); } VGAScreen = temp_surface; JE_drawOptionLevel(); } void JE_drawOptionLevel( void ) { if (twoPlayerMode) { for (temp = 1; temp <= 3; temp++) { fill_rectangle_xy(VGAScreenSeg, 268, 127 + (temp - 1) * 6, 269, 127 + 3 + (temp - 1) * 6, 193 + ((player[1].items.sidekick_level - 100) == temp) * 11); } } } void JE_tyrianHalt( JE_byte code ) { deinit_audio(); deinit_video(); deinit_joysticks(); /* TODO: NETWORK */ free_main_shape_tables(); free_sprite2s(&shapes6); for (int i = 0; i < SAMPLE_COUNT; i++) { free(digiFx[i]); } if (code != 9) { /* TODO? JE_drawANSI("exitmsg.bin"); JE_gotoXY(1,22);*/ JE_saveConfiguration(); } /* endkeyboard; */ if (code == 9) { /* OutputString('call=file0002.EXE' + #0'); TODO? */ } if (code == 5) { code = 0; } if (trentWin) { printf("\n" "\n" "\n" "\n" "Sleep well, Trent, you deserve the rest.\n" "You now have permission to borrow my ship on your next mission.\n" "\n" "Also, you might want to try out the YESXMAS parameter.\n" " Type: File0001 YESXMAS\n" "\n" "You'll need the 2.1 patch, though!\n" "\n"); } SDL_Quit(); exit(code); } void JE_initPlayerShot( JE_word portNum, uint shot_i, JE_word PX, JE_word PY, JE_word mouseX, JE_word mouseY, JE_word wpNum, JE_byte playerNum ) { const JE_byte soundChannel[11] /* [1..11] */ = {0, 2, 4, 4, 2, 2, 5, 5, 1, 4, 1}; if (portNum <= PORT_NUM) { if (wpNum > 0 && wpNum <= WEAP_NUM) { if (power >= weaponPort[portNum].poweruse) { power -= weaponPort[portNum].poweruse; if (weapons[wpNum].sound > 0) { soundQueue[soundChannel[shot_i]] = weapons[wpNum].sound; } /*Rot*/ for (tempW = 1; tempW <= weapons[wpNum].multi; tempW++) { for (b = 0; b < MAX_PWEAPON; b++) { if (shotAvail[b] == 0) { break; } } if (b == MAX_PWEAPON) { return; } if (shotMultiPos[shot_i] == weapons[wpNum].max || shotMultiPos[shot_i] > 8) { shotMultiPos[shot_i] = 1; } else { shotMultiPos[shot_i]++; } playerShotData[b].chainReaction = 0; playerShotData[b].playerNumber = playerNum; playerShotData[b].shotAni = 0; playerShotData[b].shotComplicated = weapons[wpNum].circlesize != 0; if (weapons[wpNum].circlesize == 0) { playerShotData[b].shotDevX = 0; playerShotData[b].shotDirX = 0; playerShotData[b].shotDevY = 0; playerShotData[b].shotDirY = 0; playerShotData[b].shotCirSizeX = 0; playerShotData[b].shotCirSizeY = 0; } else { temp2 = weapons[wpNum].circlesize; if (temp2 > 19) { temp3 = temp2 % 20; playerShotData[b].shotCirSizeX = temp3; playerShotData[b].shotDevX = temp3 >> 1; temp2 = temp2 / 20; playerShotData[b].shotCirSizeY = temp2; playerShotData[b].shotDevY = temp2 >> 1; } else { playerShotData[b].shotCirSizeX = temp2; playerShotData[b].shotCirSizeY = temp2; playerShotData[b].shotDevX = temp2 >> 1; playerShotData[b].shotDevY = temp2 >> 1; } playerShotData[b].shotDirX = 1; playerShotData[b].shotDirY = -1; } playerShotData[b].shotTrail = weapons[wpNum].trail; if (weapons[wpNum].attack[shotMultiPos[shot_i]-1] > 99 && weapons[wpNum].attack[shotMultiPos[shot_i]-1] < 250) { playerShotData[b].chainReaction = weapons[wpNum].attack[shotMultiPos[shot_i]-1] - 100; playerShotData[b].shotDmg = 1; } else { playerShotData[b].shotDmg = weapons[wpNum].attack[shotMultiPos[shot_i]-1]; } playerShotData[b].shotBlastFilter = weapons[wpNum].shipblastfilter; tempI = weapons[wpNum].by[shotMultiPos[shot_i]-1]; /*Note: Only front selection used for player shots...*/ playerShotData[b].shotX = PX + weapons[wpNum].bx[shotMultiPos[shot_i]-1]; playerShotData[b].shotY = PY + tempI; playerShotData[b].shotYC = -weapons[wpNum].acceleration; playerShotData[b].shotXC = weapons[wpNum].accelerationx; playerShotData[b].shotXM = weapons[wpNum].sx[shotMultiPos[shot_i]-1]; temp2 = weapons[wpNum].del[shotMultiPos[shot_i]-1]; if (temp2 == 121) { playerShotData[b].shotTrail = 0; temp2 = 255; } playerShotData[b].shotGr = weapons[wpNum].sg[shotMultiPos[shot_i]-1]; if (playerShotData[b].shotGr == 0) { shotAvail[b] = 0; } else { shotAvail[b] = temp2; } if (temp2 > 100 && temp2 < 120) { playerShotData[b].shotAniMax = (temp2 - 100 + 1); } else { playerShotData[b].shotAniMax = weapons[wpNum].weapani + 1; } if (temp2 == 99 || temp2 == 98) { tempI = PX - mouseX; if (tempI < -5) { tempI = -5; } if (tempI > 5) { tempI = 5; } playerShotData[b].shotXM += tempI; } if (temp2 == 99 || temp2 == 100) { tempI = PY - mouseY - weapons[wpNum].sy[shotMultiPos[shot_i]-1]; if (tempI < -4) { tempI = -4; } if (tempI > 4) { tempI = 4; } playerShotData[b].shotYM = tempI; } else { if (weapons[wpNum].sy[shotMultiPos[shot_i]-1] == 98) { playerShotData[b].shotYM = 0; playerShotData[b].shotYC = -1; } else { if (weapons[wpNum].sy[shotMultiPos[shot_i]-1] > 100) { playerShotData[b].shotYM = weapons[wpNum].sy[shotMultiPos[shot_i]-1]; playerShotData[b].shotY -= player[playerShotData[b].playerNumber-1].delta_y_shot_move; } else { playerShotData[b].shotYM = -weapons[wpNum].sy[shotMultiPos[shot_i]-1]; } } } if (weapons[wpNum].sx[shotMultiPos[shot_i]-1] > 100) { playerShotData[b].shotXM = weapons[wpNum].sx[shotMultiPos[shot_i]-1]; playerShotData[b].shotX -= player[playerShotData[b].playerNumber-1].delta_x_shot_move; if (playerShotData[b].shotXM == 101) { playerShotData[b].shotY -= player[playerShotData[b].playerNumber-1].delta_y_shot_move; } } if (weapons[wpNum].aim > 5) /*Guided Shot*/ { uint best_dist = 65000; temp3 = 0; /*Find Closest Enemy*/ for (x = 0; x < 100; x++) { if (enemyAvail[x] != 1 && !enemy[x].scoreitem) { y = abs(enemy[x].ex - playerShotData[b].shotX) + abs(enemy[x].ey - playerShotData[b].shotY); if (y < best_dist) { best_dist = y; temp3 = x + 1; } } } playerShotData[b].aimAtEnemy = temp3; playerShotData[b].aimDelay = 5; playerShotData[b].aimDelayMax = weapons[wpNum].aim - 5; } else { playerShotData[b].aimAtEnemy = 0; } shotRepeat[shot_i] = weapons[wpNum].shotrepeat; } } } } } void JE_specialComplete( JE_byte playerNum, JE_byte specialType ) { nextSpecialWait = 0; switch (special[specialType].stype) { /*Weapon*/ case 1: if (playerNum == 1) JE_initPlayerShot(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, special[specialType].wpn, playerNum); else JE_initPlayerShot(0, SHOT_SPECIAL2, player[1].x, player[1].y, mouseX, mouseY, special[specialType].wpn, playerNum); shotRepeat[SHOT_SPECIAL] = shotRepeat[SHOT_SPECIAL2]; break; /*Repulsor*/ case 2: for (temp = 0; temp < ENEMY_SHOT_MAX; temp++) { if (!enemyShotAvail[temp]) { if (player[0].x > enemyShot[temp].sx) enemyShot[temp].sxm--; else if (player[0].x < enemyShot[temp].sx) enemyShot[temp].sxm++; if (player[0].y > enemyShot[temp].sy) enemyShot[temp].sym--; else if (player[0].y < enemyShot[temp].sy) enemyShot[temp].sym++; } } break; /*Zinglon Blast*/ case 3: zinglonDuration = 50; shotRepeat[SHOT_SPECIAL] = 100; soundQueue[7] = S_SOUL_OF_ZINGLON; break; /*Attractor*/ case 4: for (temp = 0; temp < 100; temp++) { if (enemyAvail[temp] != 1 && enemy[temp].scoreitem && enemy[temp].evalue != 0) { if (player[0].x > enemy[temp].ex) enemy[temp].exc++; else if (player[0].x < enemy[temp].ex) enemy[temp].exc--; if (player[0].y > enemy[temp].ey) enemy[temp].eyc++; else if (player[0].y < enemy[temp].ey) enemy[temp].eyc--; } } break; /*Flare*/ case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 16: if (flareDuration == 0) flareStart = true; specialWeaponWpn = special[specialType].wpn; linkToPlayer = false; spraySpecial = false; switch (special[specialType].stype) { case 5: specialWeaponFilter = 7; specialWeaponFreq = 2; flareDuration = 50; break; case 6: specialWeaponFilter = 1; specialWeaponFreq = 7; flareDuration = 200 + 25 * player[0].items.weapon[FRONT_WEAPON].power; break; case 7: specialWeaponFilter = 3; specialWeaponFreq = 3; flareDuration = 50 + 10 * player[0].items.weapon[FRONT_WEAPON].power; zinglonDuration = 50; shotRepeat[SHOT_SPECIAL] = 100; soundQueue[7] = S_SOUL_OF_ZINGLON; break; case 8: specialWeaponFilter = -99; specialWeaponFreq = 7; flareDuration = 10 + player[0].items.weapon[FRONT_WEAPON].power; break; case 9: specialWeaponFilter = -99; specialWeaponFreq = 8; flareDuration = 8 + 2 * player[0].items.weapon[FRONT_WEAPON].power; linkToPlayer = true; nextSpecialWait = special[specialType].pwr; break; case 10: specialWeaponFilter = -99; specialWeaponFreq = 8; flareDuration = 14 + 4 * player[0].items.weapon[FRONT_WEAPON].power; linkToPlayer = true; break; case 11: specialWeaponFilter = -99; specialWeaponFreq = special[specialType].pwr; flareDuration = 10 + 10 * player[0].items.weapon[FRONT_WEAPON].power; astralDuration = 20 + 10 * player[0].items.weapon[FRONT_WEAPON].power; break; case 16: specialWeaponFilter = -99; specialWeaponFreq = 8; flareDuration = temp2 * 16 + 8; linkToPlayer = true; spraySpecial = true; break; } break; case 12: player[playerNum-1].invulnerable_ticks = temp2 * 10; if (superArcadeMode > 0 && superArcadeMode <= SA) { shotRepeat[SHOT_SPECIAL] = 250; JE_initPlayerShot(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, 707, 1); player[0].invulnerable_ticks = 100; } break; case 13: player[0].armor += temp2 / 4 + 1; soundQueue[3] = S_POWERUP; break; case 14: player[1].armor += temp2 / 4 + 1; soundQueue[3] = S_POWERUP; break; case 17: // spawn left or right sidekick soundQueue[3] = S_POWERUP; if (player[0].items.sidekick[LEFT_SIDEKICK] == special[specialType].wpn) { player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn; shotMultiPos[RIGHT_SIDEKICK] = 0; } else { player[0].items.sidekick[LEFT_SIDEKICK] = special[specialType].wpn; shotMultiPos[LEFT_SIDEKICK] = 0; } JE_drawOptions(); break; case 18: // spawn right sidekick player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn; JE_drawOptions(); soundQueue[4] = S_POWERUP; shotMultiPos[RIGHT_SIDEKICK] = 0; break; } } void JE_doSpecialShot( JE_byte playerNum, uint *armor, uint *shield ) { if (player[0].items.special > 0) { if (shotRepeat[SHOT_SPECIAL] == 0 && specialWait == 0 && flareDuration < 2 && zinglonDuration < 2) blit_sprite2(VGAScreen, 47, 4, shapes9, 94); else blit_sprite2(VGAScreen, 47, 4, shapes9, 93); } if (shotRepeat[SHOT_SPECIAL] > 0) { --shotRepeat[SHOT_SPECIAL]; } if (specialWait > 0) { specialWait--; } temp = SFExecuted[playerNum-1]; if (temp > 0 && shotRepeat[SHOT_SPECIAL] == 0 && flareDuration == 0) { temp2 = special[temp].pwr; bool can_afford = true; if (temp2 > 0) { if (temp2 < 98) // costs some shield { if (*shield >= temp2) *shield -= temp2; else can_afford = false; } else if (temp2 == 98) // costs all shield { if (*shield < 4) can_afford = false; temp2 = *shield; *shield = 0; } else if (temp2 == 99) // costs half shield { temp2 = *shield / 2; *shield = temp2; } else // costs some armor { temp2 -= 100; if (*armor > temp2) *armor -= temp2; else can_afford = false; } } shotMultiPos[SHOT_SPECIAL] = 0; shotMultiPos[SHOT_SPECIAL2] = 0; if (can_afford) JE_specialComplete(playerNum, temp); SFExecuted[playerNum-1] = 0; JE_wipeShieldArmorBars(); VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawShield(); JE_drawArmor(); VGAScreen = game_screen; /* side-effect of game_screen */ } if (playerNum == 1 && player[0].items.special > 0) { /*Main Begin*/ if (superArcadeMode > 0 && (button[2-1] || button[3-1])) { fireButtonHeld = false; } if (!button[1-1] && !(superArcadeMode != SA_NONE && (button[2-1] || button[3-1]))) { fireButtonHeld = false; } else if (shotRepeat[SHOT_SPECIAL] == 0 && !fireButtonHeld && !(flareDuration > 0) && specialWait == 0) { fireButtonHeld = true; JE_specialComplete(playerNum, player[0].items.special); } } /*Main End*/ if (astralDuration > 0) astralDuration--; shotAvail[MAX_PWEAPON-1] = 0; if (flareDuration > 1) { if (specialWeaponFilter != -99) { if (levelFilter == -99 && levelBrightness == -99) { filterActive = false; } if (!filterActive) { levelFilter = specialWeaponFilter; if (levelFilter == 7) { levelBrightness = 0; } filterActive = true; } if (mt_rand() % 2 == 0) flareColChg = -1; else flareColChg = 1; if (levelFilter == 7) { if (levelBrightness < -6) { flareColChg = 1; } if (levelBrightness > 6) { flareColChg = -1; } levelBrightness += flareColChg; } } if ((signed)(mt_rand() % 6) < specialWeaponFreq) { b = 0; if (linkToPlayer) { if (shotRepeat[SHOT_SPECIAL] == 0) { JE_initPlayerShot(0, SHOT_SPECIAL, player[0].x, player[0].y, mouseX, mouseY, specialWeaponWpn, playerNum); } } else { JE_initPlayerShot(0, SHOT_SPECIAL, mt_rand() % 280, mt_rand() % 180, mouseX, mouseY, specialWeaponWpn, playerNum); } if (spraySpecial && b > 0) { playerShotData[b].shotXM = (mt_rand() % 5) - 2; playerShotData[b].shotYM = (mt_rand() % 5) - 2; if (playerShotData[b].shotYM == 0) { playerShotData[b].shotYM++; } } } flareDuration--; if (flareDuration == 1) { specialWait = nextSpecialWait; } } else if (flareStart) { flareStart = false; shotRepeat[SHOT_SPECIAL] = linkToPlayer ? 15 : 200; flareDuration = 0; if (levelFilter == specialWeaponFilter) { levelFilter = -99; levelBrightness = -99; filterActive = false; } } if (zinglonDuration > 1) { temp = 25 - abs(zinglonDuration - 25); JE_barBright(VGAScreen, player[0].x + 7 - temp, 0, player[0].x + 7 + temp, 184); JE_barBright(VGAScreen, player[0].x + 7 - temp - 2, 0, player[0].x + 7 + temp + 2, 184); zinglonDuration--; if (zinglonDuration % 5 == 0) { shotAvail[MAX_PWEAPON-1] = 1; } } } void JE_setupExplosion( signed int x, signed int y, signed int delta_y, unsigned int type, bool fixed_position, bool follow_player ) { const struct { JE_word sprite; JE_byte ttl; } explosion_data[53] /* [1..53] */ = { { 144, 7 }, { 120, 12 }, { 190, 12 }, { 209, 12 }, { 152, 12 }, { 171, 12 }, { 133, 7 }, /*White Smoke*/ { 1, 12 }, { 20, 12 }, { 39, 12 }, { 58, 12 }, { 110, 3 }, { 76, 7 }, { 91, 3 }, /*15*/ { 227, 3 }, { 230, 3 }, { 233, 3 }, { 252, 3 }, { 246, 3 }, /*20*/ { 249, 3 }, { 265, 3 }, { 268, 3 }, { 271, 3 }, { 236, 3 }, /*25*/ { 239, 3 }, { 242, 3 }, { 261, 3 }, { 274, 3 }, { 277, 3 }, /*30*/ { 280, 3 }, { 299, 3 }, { 284, 3 }, { 287, 3 }, { 290, 3 }, /*35*/ { 293, 3 }, { 165, 8 }, /*Coin Values*/ { 184, 8 }, { 203, 8 }, { 222, 8 }, { 168, 8 }, { 187, 8 }, { 206, 8 }, { 225, 10 }, { 169, 10 }, { 188, 10 }, { 207, 20 }, { 226, 14 }, { 170, 14 }, { 189, 14 }, { 208, 14 }, { 246, 14 }, { 227, 14 }, { 265, 14 } }; if (y > -16 && y < 190) { for (int i = 0; i < MAX_EXPLOSIONS; i++) { if (explosions[i].ttl == 0) { explosions[i].x = x; explosions[i].y = y; if (type == 6) { explosions[i].y += 12; explosions[i].x += 2; } else if (type == 98) { type = 6; } explosions[i].sprite = explosion_data[type].sprite; explosions[i].ttl = explosion_data[type].ttl; explosions[i].follow_player = follow_player; explosions[i].fixed_position = fixed_position; explosions[i].delta_x = 0; explosions[i].delta_y = delta_y; break; } } } } void JE_setupExplosionLarge( JE_boolean enemyGround, JE_byte exploNum, JE_integer x, JE_integer y ) { if (y >= 0) { if (enemyGround) { JE_setupExplosion(x - 6, y - 14, 0, 2, false, false); JE_setupExplosion(x + 6, y - 14, 0, 4, false, false); JE_setupExplosion(x - 6, y, 0, 3, false, false); JE_setupExplosion(x + 6, y, 0, 5, false, false); } else { JE_setupExplosion(x - 6, y - 14, 0, 7, false, false); JE_setupExplosion(x + 6, y - 14, 0, 9, false, false); JE_setupExplosion(x - 6, y, 0, 8, false, false); JE_setupExplosion(x + 6, y, 0, 10, false, false); } bool big; if (exploNum > 10) { exploNum -= 10; big = true; } else { big = false; } if (exploNum) { for (int i = 0; i < MAX_REPEATING_EXPLOSIONS; i++) { if (rep_explosions[i].ttl == 0) { rep_explosions[i].ttl = exploNum; rep_explosions[i].delay = 2; rep_explosions[i].x = x; rep_explosions[i].y = y; rep_explosions[i].big = big; break; } } } } } void JE_wipeShieldArmorBars( void ) { if (!twoPlayerMode || galagaMode) { fill_rectangle_xy(VGAScreenSeg, 270, 137, 278, 194 - player[0].shield * 2, 0); } else { fill_rectangle_xy(VGAScreenSeg, 270, 60 - 44, 278, 60, 0); fill_rectangle_xy(VGAScreenSeg, 270, 194 - 44, 278, 194, 0); } if (!twoPlayerMode || galagaMode) { fill_rectangle_xy(VGAScreenSeg, 307, 137, 315, 194 - player[0].armor * 2, 0); } else { fill_rectangle_xy(VGAScreenSeg, 307, 60 - 44, 315, 60, 0); fill_rectangle_xy(VGAScreenSeg, 307, 194 - 44, 315, 194, 0); } } JE_byte JE_playerDamage( JE_byte temp, Player *this_player ) { int playerDamage = 0; soundQueue[7] = S_SHIELD_HIT; /* Player Damage Routines */ if (this_player->shield < temp) { playerDamage = temp; temp -= this_player->shield; this_player->shield = 0; if (temp > 0) { /*Through Shields - Now Armor */ if (this_player->armor < temp) { temp -= this_player->armor; this_player->armor = 0; if (this_player->is_alive && !youAreCheating) { levelTimer = false; this_player->is_alive = false; this_player->exploding_ticks = 60; levelEnd = 40; tempVolume = tyrMusicVolume; soundQueue[1] = S_EXPLOSION_22; } } else { this_player->armor -= temp; soundQueue[7] = S_HULL_HIT; } } } else { this_player->shield -= temp; JE_setupExplosion(this_player->x - 17, this_player->y - 12, 0, 14, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 5 , this_player->y - 12, 0, 15, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 7 , this_player->y - 12, 0, 16, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 19, this_player->y - 12, 0, 17, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 17, this_player->y + 2, 0, 18, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 19, this_player->y + 2, 0, 19, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 17, this_player->y + 16, 0, 20, false, !twoPlayerMode); JE_setupExplosion(this_player->x - 5 , this_player->y + 16, 0, 21, false, !twoPlayerMode); JE_setupExplosion(this_player->x + 7 , this_player->y + 16, 0, 22, false, !twoPlayerMode); } JE_wipeShieldArmorBars(); VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ JE_drawShield(); JE_drawArmor(); VGAScreen = game_screen; /* side-effect of game_screen */ return playerDamage; } JE_word JE_portConfigs( void ) { const uint player_index = twoPlayerMode ? 1 : 0; return tempW = weaponPort[player[player_index].items.weapon[REAR_WEAPON].id].opnum; } void JE_drawShield( void ) { if (twoPlayerMode && !galagaMode) { for (uint i = 0; i < COUNTOF(player); ++i) JE_dBar3(VGAScreen, 270, 60 + 134 * i, roundf(player[i].shield * 0.8f), 144); } else { JE_dBar3(VGAScreen, 270, 194, player[0].shield, 144); if (player[0].shield != player[0].shield_max) { const uint y = 193 - (player[0].shield_max * 2); JE_rectangle(VGAScreen, 270, y, 278, y, 68); /* SEGa000 */ } } } void JE_drawArmor( void ) { for (uint i = 0; i < COUNTOF(player); ++i) if (player[i].armor > 28) player[i].armor = 28; if (twoPlayerMode && !galagaMode) { for (uint i = 0; i < COUNTOF(player); ++i) JE_dBar3(VGAScreen, 307, 60 + 134 * i, roundf(player[i].armor * 0.8f), 224); } else { JE_dBar3(VGAScreen, 307, 194, player[0].armor, 224); } } void JE_doSP( JE_word x, JE_word y, JE_word num, JE_byte explowidth, JE_byte color ) /* superpixels */ { for (temp = 0; temp < num; temp++) { JE_real tempr = mt_rand_lt1() * (2 * M_PI); signed int tempy = roundf(cosf(tempr) * mt_rand_1() * explowidth); signed int tempx = roundf(sinf(tempr) * mt_rand_1() * explowidth); if (++last_superpixel >= MAX_SUPERPIXELS) last_superpixel = 0; superpixels[last_superpixel].x = tempx + x; superpixels[last_superpixel].y = tempy + y; superpixels[last_superpixel].delta_x = tempx; superpixels[last_superpixel].delta_y = tempy + 1; superpixels[last_superpixel].color = color; superpixels[last_superpixel].z = 15; } } void JE_drawSP( void ) { for (int i = MAX_SUPERPIXELS; i--; ) { if (superpixels[i].z) { superpixels[i].x += superpixels[i].delta_x; superpixels[i].y += superpixels[i].delta_y; if (superpixels[i].x < (unsigned)VGAScreen->w && superpixels[i].y < (unsigned)VGAScreen->h) { Uint8 *s = (Uint8 *)VGAScreen->pixels; /* screen pointer, 8-bit specific */ s += superpixels[i].y * VGAScreen->pitch; s += superpixels[i].x; *s = (((*s & 0x0f) + superpixels[i].z) >> 1) + superpixels[i].color; if (superpixels[i].x > 0) *(s - 1) = (((*(s - 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; if (superpixels[i].x < VGAScreen->w - 1u) *(s + 1) = (((*(s + 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; if (superpixels[i].y > 0) *(s - VGAScreen->pitch) = (((*(s - VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; if (superpixels[i].y < VGAScreen->h - 1u) *(s + VGAScreen->pitch) = (((*(s + VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; } superpixels[i].z--; } } } // kate: tab-width 4; vim: set noet: