forked from KolibriOS/kolibrios
501 lines
12 KiB
C
501 lines
12 KiB
C
/*
|
|
* OpenTyrian: A modern cross-platform port of Tyrian
|
|
* Copyright (C) 2007-2013 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 "shots.h"
|
|
|
|
#include "player.h"
|
|
#include "sprite.h"
|
|
#include "video.h"
|
|
#include "varz.h"
|
|
|
|
// I'm pretty sure the last extra entry is never used.
|
|
PlayerShotDataType playerShotData[MAX_PWEAPON + 1]; /* [1..MaxPWeapon+1] */
|
|
JE_byte shotAvail[MAX_PWEAPON]; /* [1..MaxPWeapon] */ /*0:Avail 1-255:Duration left*/
|
|
|
|
void simulate_player_shots( void )
|
|
{
|
|
/* Player Shot Images */
|
|
for (int z = 0; z < MAX_PWEAPON; z++)
|
|
{
|
|
if (shotAvail[z] != 0)
|
|
{
|
|
shotAvail[z]--;
|
|
if (z != MAX_PWEAPON - 1)
|
|
{
|
|
PlayerShotDataType* shot = &playerShotData[z];
|
|
|
|
shot->shotXM += shot->shotXC;
|
|
|
|
if (shot->shotXM <= 100)
|
|
shot->shotX += shot->shotXM;
|
|
|
|
shot->shotYM += shot->shotYC;
|
|
shot->shotY += shot->shotYM;
|
|
|
|
if (shot->shotYM > 100)
|
|
{
|
|
shot->shotY -= 120;
|
|
shot->shotY += player[0].delta_y_shot_move;
|
|
}
|
|
|
|
if (shot->shotComplicated != 0)
|
|
{
|
|
shot->shotDevX += shot->shotDirX;
|
|
shot->shotX += shot->shotDevX;
|
|
|
|
if (abs(shot->shotDevX) == shot->shotCirSizeX)
|
|
shot->shotDirX = -shot->shotDirX;
|
|
|
|
shot->shotDevY += shot->shotDirY;
|
|
shot->shotY += shot->shotDevY;
|
|
|
|
if (abs(shot->shotDevY) == shot->shotCirSizeY)
|
|
shot->shotDirY = -shot->shotDirY;
|
|
/*Double Speed Circle Shots - add a second copy of above loop*/
|
|
}
|
|
|
|
int tempShotX = shot->shotX;
|
|
int tempShotY = shot->shotY;
|
|
|
|
if (shot->shotX < 0 || shot->shotX > 140 ||
|
|
shot->shotY < 0 || shot->shotY > 170)
|
|
{
|
|
shotAvail[z] = 0;
|
|
goto draw_player_shot_loop_end;
|
|
}
|
|
|
|
/* if (shot->shotTrail != 255)
|
|
{
|
|
if (shot->shotTrail == 98)
|
|
{
|
|
JE_setupExplosion(shot->shotX - shot->shotXM, shot->shotY - shot->shotYM, shot->shotTrail);
|
|
} else {
|
|
JE_setupExplosion(shot->shotX, shot->shotY, shot->shotTrail);
|
|
}
|
|
}*/
|
|
|
|
JE_word anim_frame = shot->shotGr + shot->shotAni;
|
|
if (++shot->shotAni == shot->shotAniMax)
|
|
shot->shotAni = 0;
|
|
|
|
if (anim_frame < 6000)
|
|
{
|
|
if (anim_frame > 1000)
|
|
anim_frame = anim_frame % 1000;
|
|
if (anim_frame > 500)
|
|
blit_sprite2(VGAScreen, tempShotX+1, tempShotY, shapesW2, anim_frame - 500);
|
|
else
|
|
blit_sprite2(VGAScreen, tempShotX+1, tempShotY, shapesC1, anim_frame);
|
|
}
|
|
}
|
|
|
|
draw_player_shot_loop_end:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const JE_word linkMultiGr[17] /* [0..16] */ =
|
|
{77,221,183,301,1,282,164,202,58,201,163,281,39,300,182,220,77};
|
|
static const JE_word linkSonicGr[17] /* [0..16] */ =
|
|
{85,242,131,303,47,284,150,223,66,224,149,283,9,302,130,243,85};
|
|
static const JE_word linkMult2Gr[17] /* [0..16] */ =
|
|
{78,299,295,297,2,278,276,280,59,279,275,277,40,296,294,298,78};
|
|
|
|
void player_shot_set_direction( JE_integer shot_id, uint weapon_id, JE_real direction )
|
|
{
|
|
PlayerShotDataType* shot = &playerShotData[shot_id];
|
|
|
|
shot->shotXM = -roundf(sinf(direction) * shot->shotYM);
|
|
shot->shotYM = -roundf(cosf(direction) * shot->shotYM);
|
|
|
|
// Some weapons have sprites for each direction, use those.
|
|
int rounded_dir;
|
|
|
|
switch (weapon_id)
|
|
{
|
|
case 27:
|
|
case 32:
|
|
case 10:
|
|
rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/
|
|
shot->shotGr = linkMultiGr[rounded_dir];
|
|
break;
|
|
case 28:
|
|
case 33:
|
|
case 11:
|
|
rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/
|
|
shot->shotGr = linkSonicGr[rounded_dir];
|
|
break;
|
|
case 30:
|
|
case 35:
|
|
case 14:
|
|
if (direction > M_PI_2 && direction < M_PI + M_PI_2)
|
|
{
|
|
shot->shotYC = 1;
|
|
}
|
|
break;
|
|
case 38:
|
|
case 22:
|
|
rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/
|
|
shot->shotGr = linkMult2Gr[rounded_dir];
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool player_shot_move_and_draw(
|
|
int shot_id, bool* out_is_special,
|
|
int* out_shotx, int* out_shoty,
|
|
JE_integer* out_shot_damage, JE_byte* out_blast_filter,
|
|
JE_byte* out_chain, JE_byte* out_playerNum,
|
|
JE_word* out_special_radiusw, JE_word* out_special_radiush )
|
|
{
|
|
PlayerShotDataType* shot = &playerShotData[shot_id];
|
|
|
|
shotAvail[shot_id]--;
|
|
if (shot_id != MAX_PWEAPON - 1)
|
|
{
|
|
shot->shotXM += shot->shotXC;
|
|
shot->shotX += shot->shotXM;
|
|
JE_integer tmp_shotXM = shot->shotXM;
|
|
|
|
if (shot->shotXM > 100)
|
|
{
|
|
if (shot->shotXM == 101)
|
|
{
|
|
shot->shotX -= 101;
|
|
shot->shotX += player[shot->playerNumber-1].delta_x_shot_move;
|
|
shot->shotY += player[shot->playerNumber-1].delta_y_shot_move;
|
|
}
|
|
else
|
|
{
|
|
shot->shotX -= 120;
|
|
shot->shotX += player[shot->playerNumber-1].delta_x_shot_move;
|
|
}
|
|
}
|
|
|
|
shot->shotYM += shot->shotYC;
|
|
shot->shotY += shot->shotYM;
|
|
|
|
if (shot->shotYM > 100)
|
|
{
|
|
shot->shotY -= 120;
|
|
shot->shotY += player[shot->playerNumber-1].delta_y_shot_move;
|
|
}
|
|
|
|
if (shot->shotComplicated != 0)
|
|
{
|
|
shot->shotDevX += shot->shotDirX;
|
|
shot->shotX += shot->shotDevX;
|
|
|
|
if (abs(shot->shotDevX) == shot->shotCirSizeX)
|
|
shot->shotDirX = -shot->shotDirX;
|
|
|
|
shot->shotDevY += shot->shotDirY;
|
|
shot->shotY += shot->shotDevY;
|
|
|
|
if (abs(shot->shotDevY) == shot->shotCirSizeY)
|
|
shot->shotDirY = -shot->shotDirY;
|
|
|
|
/*Double Speed Circle Shots - add a second copy of above loop*/
|
|
}
|
|
|
|
*out_shotx = shot->shotX;
|
|
*out_shoty = shot->shotY;
|
|
|
|
if (shot->shotX < -34 || shot->shotX > 290 ||
|
|
shot->shotY < -15 || shot->shotY > 190)
|
|
{
|
|
shotAvail[shot_id] = 0;
|
|
return false;
|
|
}
|
|
|
|
if (shot->shotTrail != 255)
|
|
{
|
|
if (shot->shotTrail == 98)
|
|
JE_setupExplosion(shot->shotX - shot->shotXM, shot->shotY - shot->shotYM, 0, shot->shotTrail, false, false);
|
|
else
|
|
JE_setupExplosion(shot->shotX, shot->shotY, 0, shot->shotTrail, false, false);
|
|
}
|
|
|
|
if (shot->aimAtEnemy != 0)
|
|
{
|
|
if (--shot->aimDelay == 0)
|
|
{
|
|
shot->aimDelay = shot->aimDelayMax;
|
|
|
|
if (enemyAvail[shot->aimAtEnemy - 1] != 1)
|
|
{
|
|
if (shot->shotX < enemy[shot->aimAtEnemy - 1].ex)
|
|
shot->shotXM++;
|
|
else
|
|
shot->shotXM--;
|
|
|
|
if (shot->shotY < enemy[shot->aimAtEnemy - 1].ey)
|
|
shot->shotYM++;
|
|
else
|
|
shot->shotYM--;
|
|
}
|
|
else
|
|
{
|
|
if (shot->shotXM > 0)
|
|
shot->shotXM++;
|
|
else
|
|
shot->shotXM--;
|
|
}
|
|
}
|
|
}
|
|
|
|
JE_word sprite_frame = shot->shotGr + shot->shotAni;
|
|
if (++shot->shotAni == shot->shotAniMax)
|
|
shot->shotAni = 0;
|
|
|
|
*out_shot_damage = shot->shotDmg;
|
|
*out_blast_filter = shot->shotBlastFilter;
|
|
*out_chain = shot->chainReaction;
|
|
*out_playerNum = shot->playerNumber;
|
|
|
|
*out_is_special = sprite_frame > 60000;
|
|
|
|
if (*out_is_special)
|
|
{
|
|
blit_sprite_blend(VGAScreen, *out_shotx+1, *out_shoty, OPTION_SHAPES, sprite_frame - 60001);
|
|
|
|
*out_special_radiusw = sprite(OPTION_SHAPES, sprite_frame - 60001)->width / 2;
|
|
*out_special_radiush = sprite(OPTION_SHAPES, sprite_frame - 60001)->height / 2;
|
|
}
|
|
else
|
|
{
|
|
if (sprite_frame > 1000)
|
|
{
|
|
JE_doSP(*out_shotx+1 + 6, *out_shoty + 6, 5, 3, (sprite_frame / 1000) << 4);
|
|
sprite_frame = sprite_frame % 1000;
|
|
}
|
|
if (sprite_frame > 500)
|
|
{
|
|
if (background2 && *out_shoty + shadowYDist < 190 && tmp_shotXM < 100)
|
|
blit_sprite2_darken(VGAScreen, *out_shotx+1, *out_shoty + shadowYDist, shapesW2, sprite_frame - 500);
|
|
blit_sprite2(VGAScreen, *out_shotx+1, *out_shoty, shapesW2, sprite_frame - 500);
|
|
}
|
|
else
|
|
{
|
|
if (background2 && *out_shoty + shadowYDist < 190 && tmp_shotXM < 100)
|
|
blit_sprite2_darken(VGAScreen, *out_shotx+1, *out_shoty + shadowYDist, shapesC1, sprite_frame);
|
|
blit_sprite2(VGAScreen, *out_shotx+1, *out_shoty, shapesC1, sprite_frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
JE_integer player_shot_create( JE_word portNum, uint bay_i, JE_word PX, JE_word PY, JE_word mouseX, JE_word mouseY, JE_word wpNum, JE_byte playerNum )
|
|
{
|
|
static const JE_byte soundChannel[11] /* [1..11] */ = {0, 2, 4, 4, 2, 2, 5, 5, 1, 4, 1};
|
|
|
|
// Bounds check
|
|
if (portNum > PORT_NUM || wpNum <= 0 || wpNum > WEAP_NUM)
|
|
return MAX_PWEAPON;
|
|
|
|
const JE_WeaponType* weapon = &weapons[wpNum];
|
|
|
|
if (power < weaponPort[portNum].poweruse)
|
|
return MAX_PWEAPON;
|
|
power -= weaponPort[portNum].poweruse;
|
|
|
|
if (weapon->sound > 0)
|
|
soundQueue[soundChannel[bay_i]] = weapon->sound;
|
|
|
|
int shot_id = MAX_PWEAPON;
|
|
/*Rot*/
|
|
for (int multi_i = 1; multi_i <= weapon->multi; multi_i++)
|
|
{
|
|
for (shot_id = 0; shot_id < MAX_PWEAPON; shot_id++)
|
|
if (shotAvail[shot_id] == 0)
|
|
break;
|
|
if (shot_id == MAX_PWEAPON)
|
|
return MAX_PWEAPON;
|
|
|
|
if (shotMultiPos[bay_i] == weapon->max || shotMultiPos[bay_i] > 8)
|
|
shotMultiPos[bay_i] = 1;
|
|
else
|
|
shotMultiPos[bay_i]++;
|
|
|
|
PlayerShotDataType* shot = &playerShotData[shot_id];
|
|
shot->chainReaction = 0;
|
|
|
|
shot->playerNumber = playerNum;
|
|
|
|
shot->shotAni = 0;
|
|
|
|
shot->shotComplicated = weapon->circlesize != 0;
|
|
|
|
if (weapon->circlesize == 0)
|
|
{
|
|
shot->shotDevX = 0;
|
|
shot->shotDirX = 0;
|
|
shot->shotDevY = 0;
|
|
shot->shotDirY = 0;
|
|
shot->shotCirSizeX = 0;
|
|
shot->shotCirSizeY = 0;
|
|
}
|
|
else
|
|
{
|
|
JE_byte circsize = weapon->circlesize;
|
|
|
|
if (circsize > 19)
|
|
{
|
|
JE_byte circsize_mod20 = circsize % 20;
|
|
shot->shotCirSizeX = circsize_mod20;
|
|
shot->shotDevX = circsize_mod20 >> 1;
|
|
|
|
circsize = circsize / 20;
|
|
shot->shotCirSizeY = circsize;
|
|
shot->shotDevY = circsize >> 1;
|
|
}
|
|
else
|
|
{
|
|
shot->shotCirSizeX = circsize;
|
|
shot->shotCirSizeY = circsize;
|
|
shot->shotDevX = circsize >> 1;
|
|
shot->shotDevY = circsize >> 1;
|
|
}
|
|
shot->shotDirX = 1;
|
|
shot->shotDirY = -1;
|
|
}
|
|
|
|
shot->shotTrail = weapon->trail;
|
|
|
|
if (weapon->attack[shotMultiPos[bay_i]-1] > 99 && weapon->attack[shotMultiPos[bay_i]-1] < 250)
|
|
{
|
|
shot->chainReaction = weapon->attack[shotMultiPos[bay_i]-1] - 100;
|
|
shot->shotDmg = 1;
|
|
}
|
|
else
|
|
{
|
|
shot->shotDmg = weapon->attack[shotMultiPos[bay_i]-1];
|
|
}
|
|
|
|
shot->shotBlastFilter = weapon->shipblastfilter;
|
|
|
|
JE_integer tmp_by = weapon->by[shotMultiPos[bay_i]-1];
|
|
|
|
/*Note: Only front selection used for player shots...*/
|
|
|
|
shot->shotX = PX + weapon->bx[shotMultiPos[bay_i]-1];
|
|
|
|
shot->shotY = PY + tmp_by;
|
|
shot->shotYC = -weapon->acceleration;
|
|
shot->shotXC = weapon->accelerationx;
|
|
|
|
shot->shotXM = weapon->sx[shotMultiPos[bay_i]-1];
|
|
|
|
// Not sure what this field does exactly.
|
|
JE_byte del = weapon->del[shotMultiPos[bay_i]-1];
|
|
|
|
if (del == 121)
|
|
{
|
|
shot->shotTrail = 0;
|
|
del = 255;
|
|
}
|
|
|
|
shot->shotGr = weapon->sg[shotMultiPos[bay_i]-1];
|
|
if (shot->shotGr == 0)
|
|
shotAvail[shot_id] = 0;
|
|
else
|
|
shotAvail[shot_id] = del;
|
|
|
|
if (del > 100 && del < 120)
|
|
shot->shotAniMax = (del - 100 + 1);
|
|
else
|
|
shot->shotAniMax = weapon->weapani + 1;
|
|
|
|
if (del == 99 || del == 98)
|
|
{
|
|
tmp_by = PX - mouseX;
|
|
if (tmp_by < -5)
|
|
tmp_by = -5;
|
|
else if (tmp_by > 5)
|
|
tmp_by = 5;
|
|
shot->shotXM += tmp_by;
|
|
}
|
|
|
|
if (del == 99 || del == 100)
|
|
{
|
|
tmp_by = PY - mouseY - weapon->sy[shotMultiPos[bay_i]-1];
|
|
if (tmp_by < -4)
|
|
tmp_by = -4;
|
|
else if (tmp_by > 4)
|
|
tmp_by = 4;
|
|
shot->shotYM = tmp_by;
|
|
}
|
|
else if (weapon->sy[shotMultiPos[bay_i]-1] == 98)
|
|
{
|
|
shot->shotYM = 0;
|
|
shot->shotYC = -1;
|
|
}
|
|
else if (weapon->sy[shotMultiPos[bay_i]-1] > 100)
|
|
{
|
|
shot->shotYM = weapon->sy[shotMultiPos[bay_i]-1];
|
|
shot->shotY -= player[shot->playerNumber-1].delta_y_shot_move;
|
|
}
|
|
else
|
|
{
|
|
shot->shotYM = -weapon->sy[shotMultiPos[bay_i]-1];
|
|
}
|
|
|
|
if (weapon->sx[shotMultiPos[bay_i]-1] > 100)
|
|
{
|
|
shot->shotXM = weapon->sx[shotMultiPos[bay_i]-1];
|
|
shot->shotX -= player[shot->playerNumber-1].delta_x_shot_move;
|
|
if (shot->shotXM == 101)
|
|
shot->shotY -= player[shot->playerNumber-1].delta_y_shot_move;
|
|
}
|
|
|
|
|
|
if (weapon->aim > 5) /*Guided Shot*/
|
|
{
|
|
uint best_dist = 65000;
|
|
JE_byte closest_enemy = 0;
|
|
/*Find Closest Enemy*/
|
|
for (x = 0; x < 100; x++)
|
|
{
|
|
if (enemyAvail[x] != 1 && !enemy[x].scoreitem)
|
|
{
|
|
y = abs(enemy[x].ex - shot->shotX) + abs(enemy[x].ey - shot->shotY);
|
|
if (y < best_dist)
|
|
{
|
|
best_dist = y;
|
|
closest_enemy = x + 1;
|
|
}
|
|
}
|
|
}
|
|
shot->aimAtEnemy = closest_enemy;
|
|
shot->aimDelay = 5;
|
|
shot->aimDelayMax = weapon->aim - 5;
|
|
}
|
|
else
|
|
{
|
|
shot->aimAtEnemy = 0;
|
|
}
|
|
|
|
shotRepeat[bay_i] = weapon->shotrepeat;
|
|
}
|
|
|
|
return shot_id;
|
|
}
|