mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-17 09:51:39 +00:00
22082 lines
593 KiB
C++
22082 lines
593 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
Copyright (C) 1997, 2005 - 3D Realms Entertainment
|
|
|
|
This file is part of Shadow Warrior version 1.2
|
|
|
|
Shadow Warrior 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.
|
|
|
|
Original Source: 1997 - Frank Maddin and Jim Norwood
|
|
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
#include "ns.h"
|
|
#include "build.h"
|
|
#include "common.h"
|
|
|
|
#include "names2.h"
|
|
#include "panel.h"
|
|
#include "misc.h"
|
|
#include "interp.h"
|
|
#include "interpso.h"
|
|
#include "tags.h"
|
|
#include "break.h"
|
|
#include "network.h"
|
|
#include "pal.h"
|
|
|
|
#include "ai.h"
|
|
#include "weapon.h"
|
|
#include "sector.h"
|
|
#include "sprite.h"
|
|
#include "misc.h"
|
|
#include "player.h"
|
|
#include "gstrings.h"
|
|
|
|
BEGIN_SW_NS
|
|
|
|
int SpawnZombie2(short);
|
|
int move_ground_missile(short spritenum, int xchange, int ychange, int ceildist, int flordist, uint32_t cliptype, int numtics);
|
|
void DoPlayerBeginDie(PLAYERp);
|
|
void VehicleSetSmoke(SECTOR_OBJECTp sop, ANIMATORp animator);
|
|
ANIMATOR DoBettyBeginDeath;
|
|
ANIMATOR DoSkullBeginDeath;
|
|
ANIMATOR DoRipperGrow;
|
|
ANIMATOR InitMineShrap;
|
|
|
|
//
|
|
// Damage Amounts defined in damage.h
|
|
//
|
|
#define DAMAGE_TABLE
|
|
DAMAGE_DATA DamageData[] =
|
|
{
|
|
#include "damage.h"
|
|
};
|
|
#undef DAMAGE_TABLE
|
|
|
|
short ADJUST=120;
|
|
|
|
FOOT_TYPE FootMode=WATER_FOOT;
|
|
SWBOOL left_foot = FALSE;
|
|
int FinishTimer = 0;
|
|
|
|
// This is how many bullet shells have been spawned since the beginning of the game.
|
|
int ShellCount = 0;
|
|
|
|
//short Zombies = 0;
|
|
short StarQueueHead=0;
|
|
short StarQueue[MAX_STAR_QUEUE];
|
|
short HoleQueueHead=0;
|
|
short HoleQueue[MAX_HOLE_QUEUE];
|
|
short WallBloodQueueHead=0;
|
|
short WallBloodQueue[MAX_WALLBLOOD_QUEUE];
|
|
short FloorBloodQueueHead=0;
|
|
short FloorBloodQueue[MAX_FLOORBLOOD_QUEUE];
|
|
short GenericQueueHead=0;
|
|
short GenericQueue[MAX_GENERIC_QUEUE];
|
|
short LoWangsQueueHead=0;
|
|
short LoWangsQueue[MAX_LOWANGS_QUEUE];
|
|
int SpawnBreakStaticFlames(short);
|
|
|
|
SWBOOL GlobalSkipZrange = FALSE;
|
|
|
|
int WeaponIsAmmo = BIT(WPN_STAR) | BIT(WPN_SWORD) | BIT(WPN_MINE) | BIT(WPN_FIST);
|
|
|
|
short target_ang;
|
|
|
|
ANIMATOR NullAnimator;
|
|
ANIMATOR DoStar;
|
|
ANIMATOR DoCrossBolt;
|
|
ANIMATOR DoSuicide, DoUziSmoke;
|
|
ANIMATOR DoShrapJumpFall;
|
|
ANIMATOR DoFastShrapJumpFall;
|
|
|
|
int SpawnSmokePuff(short SpriteNum);
|
|
SWBOOL WarpToUnderwater(short *sectnum, int *x, int *y, int *z);
|
|
SWBOOL WarpToSurface(short *sectnum, int *x, int *y, int *z);
|
|
short ElectroFindClosestEnemy(short SpriteNum);
|
|
int InitElectroJump(SPRITEp wp, SPRITEp sp);
|
|
SWBOOL TestDontStickSector(short hit_sect);
|
|
int SpawnShrapX(short SpriteNum);
|
|
SWBOOL WeaponMoveHit(short SpriteNum);
|
|
int HelpMissileLateral(int16_t Weapon, int dist);
|
|
void SpawnMidSplash(short SpriteNum);
|
|
|
|
int SopDamage(SECTOR_OBJECTp sop,short amt);
|
|
int SopCheckKill(SECTOR_OBJECTp sop);
|
|
int QueueStar(short SpriteNum);
|
|
int DoBlurExtend(int16_t Weapon,int16_t interval,int16_t blur_num);
|
|
int SpawnDemonFist(short Weapon);
|
|
int SpawnTankShellExp(int16_t Weapon);
|
|
int SpawnMicroExp(int16_t Weapon);
|
|
void SpawnExpZadjust(short Weapon, SPRITEp exp, int upper_zsize, int lower_zsize);
|
|
int BulletHitSprite(SPRITEp sp,short hit_sprite,int hit_x,int hit_y,int hit_z,short ID);
|
|
int SpawnSplashXY(int hit_x,int hit_y,int hit_z,short);
|
|
int SpawnBoatSparks(PLAYERp pp,short hit_sect,short hit_wall,int hit_x,int hit_y,int hit_z,short hit_ang);
|
|
|
|
short StatDamageList[STAT_DAMAGE_LIST_SIZE] =
|
|
{
|
|
STAT_SO_SP_CHILD,
|
|
STAT_ENEMY,
|
|
STAT_ENEMY_SKIP4,
|
|
STAT_PLAYER0,
|
|
STAT_PLAYER1,
|
|
STAT_PLAYER2,
|
|
STAT_PLAYER3,
|
|
STAT_PLAYER4,
|
|
STAT_PLAYER5,
|
|
STAT_PLAYER6,
|
|
STAT_PLAYER7,
|
|
STAT_PLAYER_UNDER0,
|
|
STAT_PLAYER_UNDER1,
|
|
STAT_PLAYER_UNDER2,
|
|
STAT_PLAYER_UNDER3,
|
|
STAT_PLAYER_UNDER4,
|
|
STAT_PLAYER_UNDER5,
|
|
STAT_PLAYER_UNDER6,
|
|
STAT_PLAYER_UNDER7,
|
|
// MINE MUST BE LAST
|
|
STAT_MINE_STUCK
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// SPECIAL STATES
|
|
//
|
|
//////////////////////
|
|
|
|
// state for sprites that are not restored
|
|
STATE s_NotRestored[] =
|
|
{
|
|
{2323, 100, NullAnimator, &s_NotRestored[0]}
|
|
};
|
|
|
|
STATE s_Suicide[] =
|
|
{
|
|
{1, 100, DoSuicide, &s_Suicide[0]}
|
|
};
|
|
|
|
STATE s_DeadLoWang[] =
|
|
{
|
|
{1160, 100, NullAnimator, &s_DeadLoWang[0]},
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// BREAKABLE STATES
|
|
//
|
|
//////////////////////
|
|
|
|
ANIMATOR DoDefaultStat;
|
|
|
|
#define BREAK_LIGHT_RATE 18
|
|
STATE s_BreakLight[] =
|
|
{
|
|
{BREAK_LIGHT_ANIM + 0, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[1]},
|
|
{BREAK_LIGHT_ANIM + 1, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[2]},
|
|
{BREAK_LIGHT_ANIM + 2, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[3]},
|
|
{BREAK_LIGHT_ANIM + 3, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[4]},
|
|
{BREAK_LIGHT_ANIM + 4, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[5]},
|
|
{BREAK_LIGHT_ANIM + 5, BREAK_LIGHT_RATE, NullAnimator, &s_BreakLight[5]}
|
|
};
|
|
|
|
#define BREAK_BARREL_RATE 18
|
|
STATE s_BreakBarrel[] =
|
|
{
|
|
{BREAK_BARREL + 4, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[1]},
|
|
{BREAK_BARREL + 5, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[2]},
|
|
{BREAK_BARREL + 6, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[3]},
|
|
{BREAK_BARREL + 7, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[4]},
|
|
{BREAK_BARREL + 8, BREAK_BARREL_RATE, NullAnimator, &s_BreakBarrel[5]},
|
|
{BREAK_BARREL + 9, BREAK_BARREL_RATE, DoDefaultStat, &s_BreakBarrel[5]},
|
|
};
|
|
|
|
#define BREAK_PEDISTAL_RATE 28
|
|
STATE s_BreakPedistal[] =
|
|
{
|
|
{BREAK_PEDISTAL + 1, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[1]},
|
|
{BREAK_PEDISTAL + 2, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[2]},
|
|
{BREAK_PEDISTAL + 3, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[3]},
|
|
{BREAK_PEDISTAL + 4, BREAK_PEDISTAL_RATE, NullAnimator, &s_BreakPedistal[3]},
|
|
};
|
|
|
|
#define BREAK_BOTTLE1_RATE 18
|
|
STATE s_BreakBottle1[] =
|
|
{
|
|
{BREAK_BOTTLE1 + 1, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[1]},
|
|
{BREAK_BOTTLE1 + 2, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[2]},
|
|
{BREAK_BOTTLE1 + 3, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[3]},
|
|
{BREAK_BOTTLE1 + 4, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[4]},
|
|
{BREAK_BOTTLE1 + 5, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[5]},
|
|
{BREAK_BOTTLE1 + 6, BREAK_BOTTLE1_RATE, NullAnimator, &s_BreakBottle1[5]},
|
|
};
|
|
|
|
#define BREAK_BOTTLE2_RATE 18
|
|
STATE s_BreakBottle2[] =
|
|
{
|
|
{BREAK_BOTTLE2 + 1, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[1]},
|
|
{BREAK_BOTTLE2 + 2, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[2]},
|
|
{BREAK_BOTTLE2 + 3, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[3]},
|
|
{BREAK_BOTTLE2 + 4, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[4]},
|
|
{BREAK_BOTTLE2 + 5, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[5]},
|
|
{BREAK_BOTTLE2 + 6, BREAK_BOTTLE2_RATE, NullAnimator, &s_BreakBottle2[5]},
|
|
};
|
|
|
|
#define PUFF_RATE 8
|
|
ANIMATOR DoPuff;
|
|
STATE s_Puff[] =
|
|
{
|
|
{PUFF + 0, PUFF_RATE, DoPuff, &s_Puff[1]},
|
|
{PUFF + 1, PUFF_RATE, DoPuff, &s_Puff[2]},
|
|
{PUFF + 2, PUFF_RATE, DoPuff, &s_Puff[3]},
|
|
{PUFF + 3, PUFF_RATE, DoPuff, &s_Puff[4]},
|
|
{PUFF + 4, PUFF_RATE, DoPuff, &s_Puff[5]},
|
|
{PUFF + 5, PUFF_RATE, DoPuff, &s_Puff[6]},
|
|
{PUFF + 5, 100, DoSuicide, &s_Puff[0]}
|
|
};
|
|
|
|
#define RAIL_PUFF_R0 3969
|
|
#define RAIL_PUFF_R1 3985
|
|
#define RAIL_PUFF_R2 4001
|
|
#define RAIL_PUFF_RATE 6
|
|
|
|
ANIMATOR DoRailPuff;
|
|
|
|
STATE s_RailPuff[3][17] =
|
|
{
|
|
{
|
|
{RAIL_PUFF_R0 + 0, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][1]},
|
|
{RAIL_PUFF_R0 + 1, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][2]},
|
|
{RAIL_PUFF_R0 + 2, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][3]},
|
|
{RAIL_PUFF_R0 + 3, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][4]},
|
|
{RAIL_PUFF_R0 + 4, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][5]},
|
|
{RAIL_PUFF_R0 + 5, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][6]},
|
|
{RAIL_PUFF_R0 + 6, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][7]},
|
|
{RAIL_PUFF_R0 + 7, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][8]},
|
|
{RAIL_PUFF_R0 + 8, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][9]},
|
|
{RAIL_PUFF_R0 + 9, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][10]},
|
|
{RAIL_PUFF_R0 + 10, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][11]},
|
|
{RAIL_PUFF_R0 + 11, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][12]},
|
|
{RAIL_PUFF_R0 + 12, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][13]},
|
|
{RAIL_PUFF_R0 + 13, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][14]},
|
|
{RAIL_PUFF_R0 + 14, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][15]},
|
|
{RAIL_PUFF_R0 + 15, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[0][16]},
|
|
{RAIL_PUFF_R0 + 15, 100, DoSuicide, &s_RailPuff[0][0]},
|
|
},
|
|
{
|
|
{RAIL_PUFF_R1 + 0, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][1]},
|
|
{RAIL_PUFF_R1 + 1, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][2]},
|
|
{RAIL_PUFF_R1 + 2, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][3]},
|
|
{RAIL_PUFF_R1 + 3, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][4]},
|
|
{RAIL_PUFF_R1 + 4, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][5]},
|
|
{RAIL_PUFF_R1 + 5, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][6]},
|
|
{RAIL_PUFF_R1 + 6, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][7]},
|
|
{RAIL_PUFF_R1 + 7, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][8]},
|
|
{RAIL_PUFF_R1 + 8, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][9]},
|
|
{RAIL_PUFF_R1 + 9, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][10]},
|
|
{RAIL_PUFF_R1 + 10, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][11]},
|
|
{RAIL_PUFF_R1 + 11, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][12]},
|
|
{RAIL_PUFF_R1 + 12, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][13]},
|
|
{RAIL_PUFF_R1 + 13, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][14]},
|
|
{RAIL_PUFF_R1 + 14, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][15]},
|
|
{RAIL_PUFF_R1 + 15, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[1][16]},
|
|
{RAIL_PUFF_R1 + 15, 100, DoSuicide, &s_RailPuff[1][0]},
|
|
},
|
|
{
|
|
{RAIL_PUFF_R2 + 0, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][1]},
|
|
{RAIL_PUFF_R2 + 1, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][2]},
|
|
{RAIL_PUFF_R2 + 2, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][3]},
|
|
{RAIL_PUFF_R2 + 3, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][4]},
|
|
{RAIL_PUFF_R2 + 4, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][5]},
|
|
{RAIL_PUFF_R2 + 5, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][6]},
|
|
{RAIL_PUFF_R2 + 6, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][7]},
|
|
{RAIL_PUFF_R2 + 7, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][8]},
|
|
{RAIL_PUFF_R2 + 8, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][9]},
|
|
{RAIL_PUFF_R2 + 9, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][10]},
|
|
{RAIL_PUFF_R2 + 10, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][11]},
|
|
{RAIL_PUFF_R2 + 11, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][12]},
|
|
{RAIL_PUFF_R2 + 12, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][13]},
|
|
{RAIL_PUFF_R2 + 13, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][14]},
|
|
{RAIL_PUFF_R2 + 14, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][15]},
|
|
{RAIL_PUFF_R2 + 15, RAIL_PUFF_RATE, DoRailPuff, &s_RailPuff[2][16]},
|
|
{RAIL_PUFF_R2 + 15, 100, DoSuicide, &s_RailPuff[2][0]},
|
|
}
|
|
};
|
|
|
|
|
|
STATEp sg_RailPuff[] =
|
|
{
|
|
&s_RailPuff[0][0],
|
|
&s_RailPuff[1][0],
|
|
&s_RailPuff[2][0],
|
|
&s_RailPuff[1][0],
|
|
&s_RailPuff[0][0]
|
|
};
|
|
|
|
#define LASER_PUFF 3201
|
|
#define LASER_PUFF_RATE 8
|
|
STATE s_LaserPuff[] =
|
|
{
|
|
{LASER_PUFF + 0, LASER_PUFF_RATE, NULL, &s_LaserPuff[1]},
|
|
//{LASER_PUFF + 1, LASER_PUFF_RATE, NULL, &s_LaserPuff[2]},
|
|
//{LASER_PUFF + 2, LASER_PUFF_RATE, NULL, &s_LaserPuff[3]},
|
|
{LASER_PUFF + 0, 100, DoSuicide, &s_LaserPuff[0]}
|
|
};
|
|
|
|
#define TRACER 3201
|
|
#define TRACER_RATE 6
|
|
ANIMATOR DoTracer;
|
|
STATE s_Tracer[] =
|
|
{
|
|
{TRACER + 0, TRACER_RATE, DoTracer, &s_Tracer[1]},
|
|
{TRACER + 1, TRACER_RATE, DoTracer, &s_Tracer[2]},
|
|
{TRACER + 2, TRACER_RATE, DoTracer, &s_Tracer[3]},
|
|
{TRACER + 3, TRACER_RATE, DoTracer, &s_Tracer[4]},
|
|
{TRACER + 4, TRACER_RATE, DoTracer, &s_Tracer[5]},
|
|
{TRACER + 5, TRACER_RATE, DoTracer, &s_Tracer[0]}
|
|
};
|
|
|
|
#define EMP 2058
|
|
#define EMP_RATE 6
|
|
ANIMATOR DoEMP;
|
|
STATE s_EMP[] =
|
|
{
|
|
{EMP + 0, EMP_RATE, DoEMP, &s_EMP[1]},
|
|
{EMP + 1, EMP_RATE, DoEMP, &s_EMP[2]},
|
|
{EMP + 2, EMP_RATE, DoEMP, &s_EMP[0]}
|
|
};
|
|
|
|
ANIMATOR DoEMPBurst;
|
|
STATE s_EMPBurst[] =
|
|
{
|
|
{EMP + 0, EMP_RATE, DoEMPBurst, &s_EMPBurst[1]},
|
|
{EMP + 1, EMP_RATE, DoEMPBurst, &s_EMPBurst[2]},
|
|
{EMP + 2, EMP_RATE, DoEMPBurst, &s_EMPBurst[0]}
|
|
};
|
|
|
|
STATE s_EMPShrap[] =
|
|
{
|
|
{EMP + 0, EMP_RATE, DoFastShrapJumpFall, &s_EMPShrap[1]},
|
|
{EMP + 1, EMP_RATE, DoFastShrapJumpFall, &s_EMPShrap[2]},
|
|
{EMP + 2, EMP_RATE, DoFastShrapJumpFall, &s_EMPShrap[0]},
|
|
};
|
|
|
|
|
|
#define TANK_SHELL 3201
|
|
#define TANK_SHELL_RATE 6
|
|
ANIMATOR DoTankShell;
|
|
STATE s_TankShell[] =
|
|
{
|
|
{TRACER + 0, 200, DoTankShell, &s_TankShell[0]}
|
|
};
|
|
|
|
ANIMATOR DoVehicleSmoke, SpawnVehicleSmoke;
|
|
#define VEHICLE_SMOKE_RATE 18
|
|
STATE s_VehicleSmoke[] =
|
|
{
|
|
{PUFF + 0, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[1]},
|
|
{PUFF + 1, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[2]},
|
|
{PUFF + 2, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[3]},
|
|
{PUFF + 3, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[4]},
|
|
{PUFF + 4, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[5]},
|
|
{PUFF + 5, VEHICLE_SMOKE_RATE, DoVehicleSmoke, &s_VehicleSmoke[6]},
|
|
{PUFF + 5, 100, DoSuicide, &s_VehicleSmoke[6]}
|
|
};
|
|
|
|
ANIMATOR DoWaterSmoke, SpawnWaterSmoke;
|
|
#define WATER_SMOKE_RATE 18
|
|
STATE s_WaterSmoke[] =
|
|
{
|
|
{PUFF + 0, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[1]},
|
|
{PUFF + 1, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[2]},
|
|
{PUFF + 2, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[3]},
|
|
{PUFF + 3, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[4]},
|
|
{PUFF + 4, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[5]},
|
|
{PUFF + 5, WATER_SMOKE_RATE, DoWaterSmoke, &s_WaterSmoke[6]},
|
|
{PUFF + 5, 100, DoSuicide, &s_WaterSmoke[6]}
|
|
};
|
|
|
|
#define UZI_SPARK_REPEAT 24
|
|
#define UZI_SMOKE_REPEAT 24 // Was 32
|
|
#define UZI_SMOKE_RATE 16 // Was 9
|
|
STATE s_UziSmoke[] =
|
|
{
|
|
{UZI_SMOKE + 0, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[1]},
|
|
{UZI_SMOKE + 1, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[2]},
|
|
{UZI_SMOKE + 2, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[3]},
|
|
{UZI_SMOKE + 3, UZI_SMOKE_RATE, DoUziSmoke, &s_UziSmoke[4]},
|
|
{UZI_SMOKE + 3, 100, DoSuicide, &s_UziSmoke[0]},
|
|
};
|
|
|
|
#define SHOTGUN_SMOKE_RATE 16
|
|
#define SHOTGUN_SMOKE_REPEAT 18 // Was 32
|
|
#define SHOTGUN_SMOKE UZI_SMOKE+1
|
|
ANIMATOR DoShotgunSmoke;
|
|
STATE s_ShotgunSmoke[] =
|
|
{
|
|
{UZI_SMOKE + 0, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[1]},
|
|
{UZI_SMOKE + 1, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[2]},
|
|
{UZI_SMOKE + 2, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[3]},
|
|
{UZI_SMOKE + 3, SHOTGUN_SMOKE_RATE, DoShotgunSmoke, &s_ShotgunSmoke[4]},
|
|
{UZI_SMOKE + 3, 100, DoSuicide, &s_ShotgunSmoke[0]},
|
|
};
|
|
|
|
#define UZI_BULLET_RATE 100
|
|
#define UZI_BULLET 717 // actually a bubble
|
|
ANIMATOR DoUziBullet;
|
|
|
|
STATE s_UziBullet[] =
|
|
{
|
|
{UZI_BULLET + 0, UZI_BULLET_RATE, DoUziBullet, &s_UziBullet[0]},
|
|
};
|
|
|
|
#define UZI_SPARK_RATE 8
|
|
|
|
STATE s_UziSpark[] =
|
|
{
|
|
{UZI_SPARK + 0, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[1]},
|
|
{UZI_SPARK + 1, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[2]},
|
|
{UZI_SPARK + 2, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[3]},
|
|
{UZI_SPARK + 3, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[4]},
|
|
{UZI_SPARK + 4, UZI_SPARK_RATE, NullAnimator, &s_UziSpark[5]},
|
|
{UZI_SPARK + 4, 100, DoSuicide, &s_UziSpark[0]},
|
|
};
|
|
|
|
STATE s_UziPowerSpark[] =
|
|
{
|
|
{UZI_SPARK + 0, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[1]},
|
|
{UZI_SPARK + 1, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[2]},
|
|
{UZI_SPARK + 2, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[3]},
|
|
{UZI_SPARK + 3, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[4]},
|
|
{UZI_SPARK + 4, UZI_SPARK_RATE, DoUziSmoke, &s_UziSpark[5]},
|
|
{UZI_SPARK + 4, 100, DoSuicide, &s_UziSpark[0]},
|
|
};
|
|
|
|
#define BUBBLE 716
|
|
#define BUBBLE_RATE 100
|
|
ANIMATOR DoBubble;
|
|
|
|
STATE s_Bubble[] =
|
|
{
|
|
{BUBBLE + 0, BUBBLE_RATE, DoBubble, &s_Bubble[0]}
|
|
};
|
|
|
|
|
|
//#define SPLASH 2190
|
|
#define SPLASH 772
|
|
#define SPLASH_RATE 10
|
|
|
|
STATE s_Splash[] =
|
|
{
|
|
{SPLASH + 0, SPLASH_RATE, NullAnimator, &s_Splash[1]},
|
|
{SPLASH + 1, SPLASH_RATE, NullAnimator, &s_Splash[2]},
|
|
{SPLASH + 2, SPLASH_RATE, NullAnimator, &s_Splash[3]},
|
|
{SPLASH + 3, SPLASH_RATE, NullAnimator, &s_Splash[4]},
|
|
{SPLASH + 4, SPLASH_RATE, NullAnimator, &s_Splash[5]},
|
|
{SPLASH + 4, 100, DoSuicide, &s_Splash[0]}
|
|
};
|
|
|
|
#define CROSSBOLT 2230
|
|
#define CROSSBOLT_RATE 100
|
|
STATE s_CrossBolt[5][1] =
|
|
{
|
|
{
|
|
{CROSSBOLT + 0, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[0][0]},
|
|
},
|
|
{
|
|
{CROSSBOLT + 2, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[1][0]},
|
|
},
|
|
{
|
|
{CROSSBOLT + 3, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[2][0]},
|
|
},
|
|
{
|
|
{CROSSBOLT + 4, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[3][0]},
|
|
},
|
|
{
|
|
{CROSSBOLT + 1, CROSSBOLT_RATE, DoCrossBolt, &s_CrossBolt[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_CrossBolt[] =
|
|
{
|
|
&s_CrossBolt[0][0],
|
|
&s_CrossBolt[1][0],
|
|
&s_CrossBolt[2][0],
|
|
&s_CrossBolt[3][0],
|
|
&s_CrossBolt[4][0]
|
|
};
|
|
|
|
#undef STAR
|
|
#define STAR 2102
|
|
#define STAR_RATE 6
|
|
STATE s_Star[] =
|
|
{
|
|
{STAR + 0, STAR_RATE, DoStar, &s_Star[1]},
|
|
{STAR + 1, STAR_RATE, DoStar, &s_Star[2]},
|
|
{STAR + 2, STAR_RATE, DoStar, &s_Star[3]},
|
|
{STAR + 3, STAR_RATE, DoStar, &s_Star[0]}
|
|
};
|
|
|
|
STATE s_StarStuck[] =
|
|
{
|
|
{STAR + 0, STAR_RATE, NullAnimator, &s_StarStuck[0]},
|
|
};
|
|
|
|
#define STAR_DOWN 2066
|
|
STATE s_StarDown[] =
|
|
{
|
|
{STAR_DOWN + 0, STAR_RATE, DoStar, &s_StarDown[1]},
|
|
{STAR_DOWN + 1, STAR_RATE, DoStar, &s_StarDown[2]},
|
|
{STAR_DOWN + 2, STAR_RATE, DoStar, &s_StarDown[3]},
|
|
{STAR_DOWN + 3, STAR_RATE, DoStar, &s_StarDown[0]}
|
|
};
|
|
|
|
STATE s_StarDownStuck[] =
|
|
{
|
|
{STAR + 0, STAR_RATE, NullAnimator, &s_StarDownStuck[0]},
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// LAVA BOSS
|
|
//
|
|
//////////////////////
|
|
|
|
#define LAVA_BOULDER_RATE 6
|
|
ANIMATOR DoLavaBoulder,DoShrapDamage,DoVulcanBoulder;
|
|
|
|
STATE s_LavaBoulder[] =
|
|
{
|
|
{LAVA_BOULDER + 1, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[1]},
|
|
{LAVA_BOULDER + 2, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[2]},
|
|
{LAVA_BOULDER + 3, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[3]},
|
|
{LAVA_BOULDER + 4, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[4]},
|
|
{LAVA_BOULDER + 5, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[5]},
|
|
{LAVA_BOULDER + 6, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[6]},
|
|
{LAVA_BOULDER + 7, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[7]},
|
|
{LAVA_BOULDER + 8, LAVA_BOULDER_RATE, DoLavaBoulder, &s_LavaBoulder[0]},
|
|
};
|
|
|
|
#define LAVA_SHARD (LAVA_BOULDER+1)
|
|
|
|
STATE s_LavaShard[] =
|
|
{
|
|
{LAVA_BOULDER + 1, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[1]},
|
|
{LAVA_BOULDER + 2, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[2]},
|
|
{LAVA_BOULDER + 3, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[3]},
|
|
{LAVA_BOULDER + 4, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[4]},
|
|
{LAVA_BOULDER + 5, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[5]},
|
|
{LAVA_BOULDER + 6, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[6]},
|
|
{LAVA_BOULDER + 7, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[7]},
|
|
{LAVA_BOULDER + 8, LAVA_BOULDER_RATE, DoShrapDamage, &s_LavaShard[0]},
|
|
};
|
|
|
|
STATE s_VulcanBoulder[] =
|
|
{
|
|
{LAVA_BOULDER + 1, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[1]},
|
|
{LAVA_BOULDER + 2, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[2]},
|
|
{LAVA_BOULDER + 3, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[3]},
|
|
{LAVA_BOULDER + 4, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[4]},
|
|
{LAVA_BOULDER + 5, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[5]},
|
|
{LAVA_BOULDER + 6, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[6]},
|
|
{LAVA_BOULDER + 7, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[7]},
|
|
{LAVA_BOULDER + 8, LAVA_BOULDER_RATE, DoVulcanBoulder, &s_VulcanBoulder[0]},
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// GRENADE
|
|
//
|
|
//////////////////////
|
|
|
|
#if 0
|
|
ANIMATOR DoGrenade;
|
|
#undef GRENADE
|
|
#define GRENADE 5000
|
|
#define GRENADE_RATE 8
|
|
|
|
STATE s_Grenade[] =
|
|
{
|
|
{FIREBALL + 0, GRENADE_RATE, DoGrenade, &s_Grenade[1]},
|
|
{FIREBALL + 1, GRENADE_RATE, DoGrenade, &s_Grenade[2]},
|
|
{FIREBALL + 2, GRENADE_RATE, DoGrenade, &s_Grenade[3]},
|
|
{FIREBALL + 3, GRENADE_RATE, DoGrenade, &s_Grenade[0]}
|
|
};
|
|
#else
|
|
#define GRENADE_FRAMES 1
|
|
#define GRENADE_R0 2110
|
|
#define GRENADE_R1 GRENADE_R0 + (GRENADE_FRAMES * 1)
|
|
#define GRENADE_R2 GRENADE_R0 + (GRENADE_FRAMES * 2)
|
|
#define GRENADE_R3 GRENADE_R0 + (GRENADE_FRAMES * 3)
|
|
#define GRENADE_R4 GRENADE_R0 + (GRENADE_FRAMES * 4)
|
|
|
|
#undef GRENADE
|
|
#define GRENADE GRENADE_R0
|
|
#define GRENADE_RATE 8
|
|
ANIMATOR DoGrenade;
|
|
|
|
STATE s_Grenade[5][1] =
|
|
{
|
|
{
|
|
{GRENADE_R0 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[0][0]},
|
|
},
|
|
{
|
|
{GRENADE_R1 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[1][0]},
|
|
},
|
|
{
|
|
{GRENADE_R2 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[2][0]},
|
|
},
|
|
{
|
|
{GRENADE_R3 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[3][0]},
|
|
},
|
|
{
|
|
{GRENADE_R4 + 0, GRENADE_RATE, DoGrenade, &s_Grenade[4][0]},
|
|
}
|
|
};
|
|
|
|
|
|
STATEp sg_Grenade[] =
|
|
{
|
|
&s_Grenade[0][0],
|
|
&s_Grenade[1][0],
|
|
&s_Grenade[2][0],
|
|
&s_Grenade[3][0],
|
|
&s_Grenade[4][0]
|
|
};
|
|
#endif
|
|
|
|
//////////////////////
|
|
//
|
|
// MINE
|
|
//
|
|
//////////////////////
|
|
|
|
ANIMATOR DoMine,DoMineStuck;
|
|
#undef MINE
|
|
#define MINE 2223
|
|
#define MINE_SHRAP 5011
|
|
#define MINE_RATE 16
|
|
|
|
STATE s_MineStuck[] =
|
|
{
|
|
{MINE + 0, MINE_RATE, DoMineStuck, &s_MineStuck[0]},
|
|
};
|
|
|
|
STATE s_Mine[] =
|
|
{
|
|
{MINE + 0, MINE_RATE, DoMine, &s_Mine[1]},
|
|
{MINE + 1, MINE_RATE, DoMine, &s_Mine[0]},
|
|
};
|
|
|
|
ANIMATOR DoMineSpark;
|
|
STATE s_MineSpark[] =
|
|
{
|
|
{UZI_SPARK + 0, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[1]},
|
|
{UZI_SPARK + 1, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[2]},
|
|
{UZI_SPARK + 2, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[3]},
|
|
{UZI_SPARK + 3, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[4]},
|
|
{UZI_SPARK + 4, UZI_SPARK_RATE, DoMineSpark, &s_MineSpark[5]},
|
|
{UZI_SPARK + 4, 100, DoSuicide, &s_MineSpark[0]},
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// METEOR
|
|
//
|
|
//////////////////////
|
|
|
|
#define METEOR_R0 2098
|
|
#define METEOR_R1 2090
|
|
#define METEOR_R2 2094
|
|
#define METEOR_R3 2090
|
|
#define METEOR_R4 2098
|
|
|
|
|
|
#define METEOR STAR
|
|
#define METEOR_RATE 8
|
|
ANIMATOR DoMeteor;
|
|
|
|
STATE s_Meteor[5][4] =
|
|
{
|
|
{
|
|
{METEOR_R0 + 0, METEOR_RATE, DoMeteor, &s_Meteor[0][1]},
|
|
{METEOR_R0 + 1, METEOR_RATE, DoMeteor, &s_Meteor[0][2]},
|
|
{METEOR_R0 + 2, METEOR_RATE, DoMeteor, &s_Meteor[0][3]},
|
|
{METEOR_R0 + 3, METEOR_RATE, DoMeteor, &s_Meteor[0][0]},
|
|
},
|
|
{
|
|
{METEOR_R1 + 0, METEOR_RATE, DoMeteor, &s_Meteor[1][1]},
|
|
{METEOR_R1 + 1, METEOR_RATE, DoMeteor, &s_Meteor[1][2]},
|
|
{METEOR_R1 + 2, METEOR_RATE, DoMeteor, &s_Meteor[1][3]},
|
|
{METEOR_R1 + 3, METEOR_RATE, DoMeteor, &s_Meteor[1][0]},
|
|
},
|
|
{
|
|
{METEOR_R2 + 0, METEOR_RATE, DoMeteor, &s_Meteor[2][1]},
|
|
{METEOR_R2 + 1, METEOR_RATE, DoMeteor, &s_Meteor[2][2]},
|
|
{METEOR_R2 + 2, METEOR_RATE, DoMeteor, &s_Meteor[2][3]},
|
|
{METEOR_R2 + 3, METEOR_RATE, DoMeteor, &s_Meteor[2][0]},
|
|
},
|
|
{
|
|
{METEOR_R3 + 0, METEOR_RATE, DoMeteor, &s_Meteor[3][1]},
|
|
{METEOR_R3 + 1, METEOR_RATE, DoMeteor, &s_Meteor[3][2]},
|
|
{METEOR_R3 + 2, METEOR_RATE, DoMeteor, &s_Meteor[3][3]},
|
|
{METEOR_R3 + 3, METEOR_RATE, DoMeteor, &s_Meteor[3][0]},
|
|
},
|
|
{
|
|
{METEOR_R4 + 0, METEOR_RATE, DoMeteor, &s_Meteor[4][1]},
|
|
{METEOR_R4 + 1, METEOR_RATE, DoMeteor, &s_Meteor[4][2]},
|
|
{METEOR_R4 + 2, METEOR_RATE, DoMeteor, &s_Meteor[4][3]},
|
|
{METEOR_R4 + 3, METEOR_RATE, DoMeteor, &s_Meteor[4][0]},
|
|
}
|
|
};
|
|
|
|
|
|
STATEp sg_Meteor[] =
|
|
{
|
|
&s_Meteor[0][0],
|
|
&s_Meteor[1][0],
|
|
&s_Meteor[2][0],
|
|
&s_Meteor[3][0],
|
|
&s_Meteor[4][0]
|
|
};
|
|
|
|
#define METEOR_EXP 2115
|
|
#define METEOR_EXP_RATE 7
|
|
|
|
STATE s_MeteorExp[] =
|
|
{
|
|
{METEOR_EXP + 0, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[1]},
|
|
{METEOR_EXP + 1, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[2]},
|
|
{METEOR_EXP + 2, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[3]},
|
|
{METEOR_EXP + 3, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[4]},
|
|
{METEOR_EXP + 4, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[5]},
|
|
{METEOR_EXP + 5, METEOR_EXP_RATE, NullAnimator, &s_MeteorExp[6]},
|
|
{METEOR_EXP + 5, METEOR_EXP_RATE, DoSuicide, &s_MeteorExp[6]}
|
|
};
|
|
|
|
#define MIRV_METEOR METEOR_R0
|
|
ANIMATOR DoMirvMissile;
|
|
STATE s_MirvMeteor[5][4] =
|
|
{
|
|
{
|
|
{METEOR_R0 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][1]},
|
|
{METEOR_R0 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][2]},
|
|
{METEOR_R0 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][3]},
|
|
{METEOR_R0 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[0][0]},
|
|
},
|
|
{
|
|
{METEOR_R1 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][1]},
|
|
{METEOR_R1 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][2]},
|
|
{METEOR_R1 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][3]},
|
|
{METEOR_R1 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[1][0]},
|
|
},
|
|
{
|
|
{METEOR_R2 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][1]},
|
|
{METEOR_R2 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][2]},
|
|
{METEOR_R2 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][3]},
|
|
{METEOR_R2 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[2][0]},
|
|
},
|
|
{
|
|
{METEOR_R3 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][1]},
|
|
{METEOR_R3 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][2]},
|
|
{METEOR_R3 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][3]},
|
|
{METEOR_R3 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[3][0]},
|
|
},
|
|
{
|
|
{METEOR_R4 + 0, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][1]},
|
|
{METEOR_R4 + 1, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][2]},
|
|
{METEOR_R4 + 2, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][3]},
|
|
{METEOR_R4 + 3, METEOR_RATE, DoMirvMissile, &s_MirvMeteor[4][0]},
|
|
}
|
|
};
|
|
|
|
|
|
STATEp sg_MirvMeteor[] =
|
|
{
|
|
&s_MirvMeteor[0][0],
|
|
&s_MirvMeteor[1][0],
|
|
&s_MirvMeteor[2][0],
|
|
&s_MirvMeteor[3][0],
|
|
&s_MirvMeteor[4][0]
|
|
};
|
|
|
|
STATE s_MirvMeteorExp[] =
|
|
{
|
|
{METEOR_EXP + 0, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[1]},
|
|
{METEOR_EXP + 1, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[2]},
|
|
{METEOR_EXP + 2, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[3]},
|
|
{METEOR_EXP + 3, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[4]},
|
|
{METEOR_EXP + 4, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[5]},
|
|
{METEOR_EXP + 5, METEOR_EXP_RATE, NullAnimator, &s_MirvMeteorExp[6]},
|
|
{METEOR_EXP + 5, METEOR_EXP_RATE, DoSuicide, &s_MirvMeteorExp[6]}
|
|
};
|
|
|
|
#define SERP_METEOR METEOR_R0+1
|
|
ANIMATOR DoSerpMeteor;
|
|
STATE s_SerpMeteor[5][4] =
|
|
{
|
|
{
|
|
{2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][1]},
|
|
{2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][2]},
|
|
{2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][3]},
|
|
{2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[0][0]},
|
|
},
|
|
{
|
|
{2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][1]},
|
|
{2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][2]},
|
|
{2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][3]},
|
|
{2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[1][0]},
|
|
},
|
|
{
|
|
{2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][1]},
|
|
{2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][2]},
|
|
{2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][3]},
|
|
{2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[2][0]},
|
|
},
|
|
{
|
|
{2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][1]},
|
|
{2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][2]},
|
|
{2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][3]},
|
|
{2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[3][0]},
|
|
},
|
|
{
|
|
{2031 + 0, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][1]},
|
|
{2031 + 1, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][2]},
|
|
{2031 + 2, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][3]},
|
|
{2031 + 3, METEOR_RATE, DoSerpMeteor, &s_SerpMeteor[4][0]},
|
|
}
|
|
};
|
|
|
|
|
|
STATEp sg_SerpMeteor[] =
|
|
{
|
|
&s_SerpMeteor[0][0],
|
|
&s_SerpMeteor[1][0],
|
|
&s_SerpMeteor[2][0],
|
|
&s_SerpMeteor[3][0],
|
|
&s_SerpMeteor[4][0]
|
|
};
|
|
|
|
|
|
STATE s_SerpMeteorExp[] =
|
|
{
|
|
{METEOR_EXP + 0, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[1]},
|
|
{METEOR_EXP + 1, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[2]},
|
|
{METEOR_EXP + 2, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[3]},
|
|
{METEOR_EXP + 3, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[4]},
|
|
{METEOR_EXP + 4, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[5]},
|
|
{METEOR_EXP + 5, METEOR_EXP_RATE, NullAnimator, &s_SerpMeteorExp[6]},
|
|
{METEOR_EXP + 5, METEOR_EXP_RATE, DoSuicide, &s_SerpMeteorExp[6]}
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// SPEAR
|
|
//
|
|
//////////////////////
|
|
|
|
#define SPEAR_RATE 8
|
|
ANIMATOR DoSpear;
|
|
|
|
STATE s_Spear[5][1] =
|
|
{
|
|
{
|
|
{SPEAR_R0 + 0, SPEAR_RATE, DoSpear, s_Spear[0]},
|
|
},
|
|
{
|
|
{SPEAR_R1 + 0, SPEAR_RATE, DoSpear, s_Spear[1]},
|
|
},
|
|
{
|
|
{SPEAR_R2 + 0, SPEAR_RATE, DoSpear, s_Spear[2]},
|
|
},
|
|
{
|
|
{SPEAR_R3 + 0, SPEAR_RATE, DoSpear, s_Spear[3]},
|
|
},
|
|
{
|
|
{SPEAR_R4 + 0, SPEAR_RATE, DoSpear, s_Spear[4]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_Spear[] =
|
|
{
|
|
s_Spear[0],
|
|
s_Spear[1],
|
|
s_Spear[2],
|
|
s_Spear[3],
|
|
s_Spear[4]
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// ROCKET
|
|
//
|
|
//////////////////////
|
|
|
|
#define ROCKET_FRAMES 1
|
|
#define ROCKET_R0 2206
|
|
#define ROCKET_R1 ROCKET_R0 + (ROCKET_FRAMES * 2)
|
|
#define ROCKET_R2 ROCKET_R0 + (ROCKET_FRAMES * 3)
|
|
#define ROCKET_R3 ROCKET_R0 + (ROCKET_FRAMES * 4)
|
|
#define ROCKET_R4 ROCKET_R0 + (ROCKET_FRAMES * 1)
|
|
|
|
ANIMATOR DoRocket;
|
|
#define ROCKET_RATE 8
|
|
|
|
STATE s_Rocket[5][1] =
|
|
{
|
|
{
|
|
{ROCKET_R0 + 0, ROCKET_RATE, DoRocket, &s_Rocket[0][0]},
|
|
},
|
|
{
|
|
{ROCKET_R1 + 0, ROCKET_RATE, DoRocket, &s_Rocket[1][0]},
|
|
},
|
|
{
|
|
{ROCKET_R2 + 0, ROCKET_RATE, DoRocket, &s_Rocket[2][0]},
|
|
},
|
|
{
|
|
{ROCKET_R3 + 0, ROCKET_RATE, DoRocket, &s_Rocket[3][0]},
|
|
},
|
|
{
|
|
{ROCKET_R4 + 0, ROCKET_RATE, DoRocket, &s_Rocket[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_Rocket[] =
|
|
{
|
|
&s_Rocket[0][0],
|
|
&s_Rocket[1][0],
|
|
&s_Rocket[2][0],
|
|
&s_Rocket[3][0],
|
|
&s_Rocket[4][0]
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// BUNNY ROCKET
|
|
//
|
|
//////////////////////
|
|
|
|
#define BUNNYROCKET_FRAMES 5
|
|
#define BUNNYROCKET_R0 4550
|
|
#define BUNNYROCKET_R1 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 1)
|
|
#define BUNNYROCKET_R2 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 2)
|
|
#define BUNNYROCKET_R3 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 3)
|
|
#define BUNNYROCKET_R4 BUNNYROCKET_R0 + (BUNNYROCKET_FRAMES * 4)
|
|
|
|
ANIMATOR DoRocket;
|
|
#define BUNNYROCKET_RATE 8
|
|
|
|
STATE s_BunnyRocket[5][1] =
|
|
{
|
|
{
|
|
{BUNNYROCKET_R0 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[0][0]},
|
|
},
|
|
{
|
|
{BUNNYROCKET_R1 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[1][0]},
|
|
},
|
|
{
|
|
{BUNNYROCKET_R2 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[2][0]},
|
|
},
|
|
{
|
|
{BUNNYROCKET_R3 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[3][0]},
|
|
},
|
|
{
|
|
{BUNNYROCKET_R4 + 2, BUNNYROCKET_RATE, DoRocket, &s_BunnyRocket[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_BunnyRocket[] =
|
|
{
|
|
&s_BunnyRocket[0][0],
|
|
&s_BunnyRocket[1][0],
|
|
&s_BunnyRocket[2][0],
|
|
&s_BunnyRocket[3][0],
|
|
&s_BunnyRocket[4][0]
|
|
};
|
|
|
|
ANIMATOR DoRail;
|
|
#define RAIL_RATE 8
|
|
|
|
STATE s_Rail[5][1] =
|
|
{
|
|
{
|
|
{ROCKET_R0 + 0, RAIL_RATE, DoRail, &s_Rail[0][0]},
|
|
},
|
|
{
|
|
{ROCKET_R1 + 0, RAIL_RATE, DoRail, &s_Rail[1][0]},
|
|
},
|
|
{
|
|
{ROCKET_R2 + 0, RAIL_RATE, DoRail, &s_Rail[2][0]},
|
|
},
|
|
{
|
|
{ROCKET_R3 + 0, RAIL_RATE, DoRail, &s_Rail[3][0]},
|
|
},
|
|
{
|
|
{ROCKET_R4 + 0, RAIL_RATE, DoRail, &s_Rail[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_Rail[] =
|
|
{
|
|
&s_Rail[0][0],
|
|
&s_Rail[1][0],
|
|
&s_Rail[2][0],
|
|
&s_Rail[3][0],
|
|
&s_Rail[4][0]
|
|
};
|
|
|
|
ANIMATOR DoLaser;
|
|
#define LASER_RATE 8
|
|
|
|
STATE s_Laser[] =
|
|
{
|
|
{ROCKET_R0 + 0, LASER_RATE, DoLaser, &s_Laser[0]}
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// MICRO
|
|
//
|
|
//////////////////////
|
|
|
|
#define MICRO_FRAMES 1
|
|
#define MICRO_R0 2206
|
|
#define MICRO_R1 MICRO_R0 + (MICRO_FRAMES * 2)
|
|
#define MICRO_R2 MICRO_R0 + (MICRO_FRAMES * 3)
|
|
#define MICRO_R3 MICRO_R0 + (MICRO_FRAMES * 4)
|
|
#define MICRO_R4 MICRO_R0 + (MICRO_FRAMES * 1)
|
|
|
|
ANIMATOR DoMicro;
|
|
#define MICRO_RATE 8
|
|
|
|
#if 0
|
|
#define PUFF 1748
|
|
#define PUFF_RATE 8
|
|
ANIMATOR DoMicroPuffSuicide;
|
|
STATE s_MicroPuff[] =
|
|
{
|
|
{PUFF + 0, PUFF_RATE, DoPuff, &s_MicroPuff[1]},
|
|
{PUFF + 1, PUFF_RATE, DoPuff, &s_MicroPuff[2]},
|
|
{PUFF + 2, PUFF_RATE, DoPuff, &s_MicroPuff[3]},
|
|
{PUFF + 3, PUFF_RATE, DoPuff, &s_MicroPuff[4]},
|
|
{PUFF + 4, PUFF_RATE, DoPuff, &s_MicroPuff[5]},
|
|
{PUFF + 5, PUFF_RATE, DoPuff, &s_MicroPuff[6]},
|
|
{PUFF + 5, 100, DoMicroPuffSuicide, &s_MicroPuff[6]}
|
|
};
|
|
#endif
|
|
|
|
STATE s_Micro[5][1] =
|
|
{
|
|
{
|
|
{MICRO_R0 + 0, MICRO_RATE, DoMicro, &s_Micro[0][0]},
|
|
},
|
|
{
|
|
{MICRO_R1 + 0, MICRO_RATE, DoMicro, &s_Micro[1][0]},
|
|
},
|
|
{
|
|
{MICRO_R2 + 0, MICRO_RATE, DoMicro, &s_Micro[2][0]},
|
|
},
|
|
{
|
|
{MICRO_R3 + 0, MICRO_RATE, DoMicro, &s_Micro[3][0]},
|
|
},
|
|
{
|
|
{MICRO_R4 + 0, MICRO_RATE, DoMicro, &s_Micro[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_Micro[] =
|
|
{
|
|
&s_Micro[0][0],
|
|
&s_Micro[1][0],
|
|
&s_Micro[2][0],
|
|
&s_Micro[3][0],
|
|
&s_Micro[4][0]
|
|
};
|
|
|
|
ANIMATOR DoMicroMini;
|
|
STATE s_MicroMini[5][1] =
|
|
{
|
|
{
|
|
{MICRO_R0 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[0][0]},
|
|
},
|
|
{
|
|
{MICRO_R1 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[1][0]},
|
|
},
|
|
{
|
|
{MICRO_R2 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[2][0]},
|
|
},
|
|
{
|
|
{MICRO_R3 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[3][0]},
|
|
},
|
|
{
|
|
{MICRO_R4 + 0, MICRO_RATE, DoMicroMini, &s_MicroMini[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_MicroMini[] =
|
|
{
|
|
&s_MicroMini[0][0],
|
|
&s_MicroMini[1][0],
|
|
&s_MicroMini[2][0],
|
|
&s_MicroMini[3][0],
|
|
&s_MicroMini[4][0]
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// BOLT THINMAN
|
|
//
|
|
//////////////////////
|
|
|
|
#define BOLT_THINMAN_RATE 8
|
|
ANIMATOR DoBoltThinMan;
|
|
|
|
STATE s_BoltThinMan[5][1] =
|
|
{
|
|
{
|
|
{BOLT_THINMAN_R0 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[0][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R1 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[1][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R2 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[2][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R3 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[3][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R4 + 0, BOLT_THINMAN_RATE, DoBoltThinMan, &s_BoltThinMan[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_BoltThinMan[] =
|
|
{
|
|
&s_BoltThinMan[0][0],
|
|
&s_BoltThinMan[1][0],
|
|
&s_BoltThinMan[2][0],
|
|
&s_BoltThinMan[3][0],
|
|
&s_BoltThinMan[4][0]
|
|
};
|
|
|
|
#define BOLT_SEEKER_RATE 8
|
|
ANIMATOR DoBoltSeeker;
|
|
|
|
STATE s_BoltSeeker[5][1] =
|
|
{
|
|
{
|
|
{BOLT_THINMAN_R0 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[0][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R1 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[1][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R2 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[2][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R3 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[3][0]},
|
|
},
|
|
{
|
|
{BOLT_THINMAN_R4 + 0, BOLT_SEEKER_RATE, DoBoltSeeker, &s_BoltSeeker[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_BoltSeeker[] =
|
|
{
|
|
&s_BoltSeeker[0][0],
|
|
&s_BoltSeeker[1][0],
|
|
&s_BoltSeeker[2][0],
|
|
&s_BoltSeeker[3][0],
|
|
&s_BoltSeeker[4][0]
|
|
};
|
|
|
|
|
|
#define BOLT_FATMAN STAR
|
|
#define BOLT_FATMAN_RATE 8
|
|
ANIMATOR DoBoltFatMan;
|
|
|
|
STATE s_BoltFatMan[] =
|
|
{
|
|
{BOLT_FATMAN + 0, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[1]},
|
|
{BOLT_FATMAN + 1, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[2]},
|
|
{BOLT_FATMAN + 2, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[3]},
|
|
{BOLT_FATMAN + 3, BOLT_FATMAN_RATE, DoBoltFatMan, &s_BoltFatMan[0]}
|
|
};
|
|
|
|
|
|
#define BOLT_SHRAPNEL STAR
|
|
#define BOLT_SHRAPNEL_RATE 8
|
|
ANIMATOR DoBoltShrapnel;
|
|
|
|
STATE s_BoltShrapnel[] =
|
|
{
|
|
{BOLT_SHRAPNEL + 0, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[1]},
|
|
{BOLT_SHRAPNEL + 1, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[2]},
|
|
{BOLT_SHRAPNEL + 2, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[3]},
|
|
{BOLT_SHRAPNEL + 3, BOLT_SHRAPNEL_RATE, DoBoltShrapnel, &s_BoltShrapnel[0]}
|
|
};
|
|
|
|
#define COOLG_FIRE 2430
|
|
//#define COOLG_FIRE 1465
|
|
#define COOLG_FIRE_RATE 8
|
|
ANIMATOR DoCoolgFire;
|
|
|
|
STATE s_CoolgFire[] =
|
|
{
|
|
{2031 + 0, COOLG_FIRE_RATE, DoCoolgFire, &s_CoolgFire[1]},
|
|
{2031 + 1, COOLG_FIRE_RATE, DoCoolgFire, &s_CoolgFire[2]},
|
|
{2031 + 2, COOLG_FIRE_RATE, DoCoolgFire, &s_CoolgFire[3]},
|
|
{2031 + 3, COOLG_FIRE_RATE, DoCoolgFire, &s_CoolgFire[0]}
|
|
};
|
|
|
|
#define COOLG_FIRE_DONE 2410
|
|
//#define COOLG_FIRE_DONE 1466
|
|
//#define COOLG_FIRE_DONE_RATE 8
|
|
#define COOLG_FIRE_DONE_RATE 3
|
|
|
|
STATE s_CoolgFireDone[] =
|
|
{
|
|
{COOLG_FIRE_DONE + 0, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[1]},
|
|
{COOLG_FIRE_DONE + 1, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[2]},
|
|
{COOLG_FIRE_DONE + 2, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[3]},
|
|
{COOLG_FIRE_DONE + 3, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[4]},
|
|
{COOLG_FIRE_DONE + 4, COOLG_FIRE_DONE_RATE, NullAnimator, &s_CoolgFireDone[5]},
|
|
{COOLG_FIRE_DONE + 4, COOLG_FIRE_DONE_RATE, DoSuicide, &s_CoolgFireDone[5]}
|
|
};
|
|
|
|
ANIMATOR DoCoolgDrip;
|
|
#define COOLG_DRIP 1720
|
|
STATE s_CoolgDrip[] =
|
|
{
|
|
{COOLG_DRIP + 0, 100, DoCoolgDrip, &s_CoolgDrip[0]}
|
|
};
|
|
|
|
#define GORE_FLOOR_SPLASH_RATE 8
|
|
#define GORE_FLOOR_SPLASH 1710
|
|
STATE s_GoreFloorSplash[] =
|
|
{
|
|
{GORE_FLOOR_SPLASH + 0, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[1]},
|
|
{GORE_FLOOR_SPLASH + 1, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[2]},
|
|
{GORE_FLOOR_SPLASH + 2, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[3]},
|
|
{GORE_FLOOR_SPLASH + 3, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[4]},
|
|
{GORE_FLOOR_SPLASH + 4, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[5]},
|
|
{GORE_FLOOR_SPLASH + 5, GORE_FLOOR_SPLASH_RATE, NullAnimator, &s_GoreFloorSplash[6]},
|
|
{GORE_FLOOR_SPLASH + 5, GORE_FLOOR_SPLASH_RATE, DoSuicide, &s_GoreFloorSplash[6]}
|
|
};
|
|
|
|
#define GORE_SPLASH_RATE 8
|
|
#define GORE_SPLASH 2410
|
|
STATE s_GoreSplash[] =
|
|
{
|
|
{GORE_SPLASH + 0, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[1]},
|
|
{GORE_SPLASH + 1, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[2]},
|
|
{GORE_SPLASH + 2, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[3]},
|
|
{GORE_SPLASH + 3, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[4]},
|
|
{GORE_SPLASH + 4, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[5]},
|
|
{GORE_SPLASH + 5, GORE_SPLASH_RATE, NullAnimator, &s_GoreSplash[6]},
|
|
{GORE_SPLASH + 5, GORE_SPLASH_RATE, DoSuicide, &s_GoreSplash[6]}
|
|
};
|
|
|
|
//////////////////////////////////////////////
|
|
//
|
|
// HEART ATTACK & PLASMA
|
|
//
|
|
//////////////////////////////////////////////
|
|
|
|
#define PLASMA 1562 //2058
|
|
#define PLASMA_FOUNTAIN 2058+1
|
|
#define PLASMA_RATE 8
|
|
#define PLASMA_FOUNTAIN_TIME (3*120);
|
|
|
|
ANIMATOR DoPlasma;
|
|
|
|
// regular bolt from heart
|
|
STATE s_Plasma[] =
|
|
{
|
|
{PLASMA + 0, PLASMA_RATE, DoPlasma, &s_Plasma[1]},
|
|
{PLASMA + 1, PLASMA_RATE, DoPlasma, &s_Plasma[2]},
|
|
{PLASMA + 2, PLASMA_RATE, DoPlasma, &s_Plasma[0]}
|
|
};
|
|
|
|
ANIMATOR DoPlasmaFountain;
|
|
|
|
// follows actor spewing blood
|
|
#define PLASMA_Drip 1562 //2420
|
|
STATE s_PlasmaFountain[] =
|
|
{
|
|
{PLASMA_Drip + 0, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[1]},
|
|
{PLASMA_Drip + 1, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[2]},
|
|
{PLASMA_Drip + 2, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[3]},
|
|
{PLASMA_Drip + 3, PLASMA_RATE, DoPlasmaFountain, &s_PlasmaFountain[0]},
|
|
};
|
|
|
|
#define PLASMA_Drip_RATE 12
|
|
STATE s_PlasmaDrip[] =
|
|
{
|
|
{PLASMA_Drip + 0, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[1]},
|
|
{PLASMA_Drip + 1, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[2]},
|
|
{PLASMA_Drip + 2, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[3]},
|
|
{PLASMA_Drip + 3, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[4]},
|
|
{PLASMA_Drip + 4, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[5]},
|
|
{PLASMA_Drip + 5, PLASMA_Drip_RATE, DoShrapJumpFall, &s_PlasmaDrip[6]},
|
|
{PLASMA_Drip + 7, PLASMA_Drip_RATE, DoSuicide, &s_PlasmaDrip[6]}
|
|
};
|
|
|
|
#define PLASMA_DONE 2061
|
|
#define PLASMA_DONE_RATE 15
|
|
|
|
ANIMATOR DoPlasmaDone;
|
|
|
|
STATE s_PlasmaDone[] =
|
|
{
|
|
{PLASMA + 0, PLASMA_DONE_RATE, DoPlasmaDone, &s_PlasmaDone[1]},
|
|
{PLASMA + 2, PLASMA_DONE_RATE, DoPlasmaDone, &s_PlasmaDone[2]},
|
|
{PLASMA + 1, PLASMA_DONE_RATE, DoPlasmaDone, &s_PlasmaDone[0]}
|
|
};
|
|
|
|
#define TELEPORT_EFFECT 3240
|
|
#define TELEPORT_EFFECT_RATE 6
|
|
ANIMATOR DoTeleportEffect;
|
|
STATE s_TeleportEffect[] =
|
|
{
|
|
{TELEPORT_EFFECT + 0, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[1]},
|
|
{TELEPORT_EFFECT + 1, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[2]},
|
|
{TELEPORT_EFFECT + 2, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[3]},
|
|
{TELEPORT_EFFECT + 3, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[4]},
|
|
{TELEPORT_EFFECT + 4, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[5]},
|
|
{TELEPORT_EFFECT + 5, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[6]},
|
|
{TELEPORT_EFFECT + 6, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[7]},
|
|
{TELEPORT_EFFECT + 7, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[8]},
|
|
{TELEPORT_EFFECT + 8, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[9]},
|
|
{TELEPORT_EFFECT + 9, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[10]},
|
|
{TELEPORT_EFFECT + 10, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[11]},
|
|
{TELEPORT_EFFECT + 11, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[12]},
|
|
{TELEPORT_EFFECT + 12, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[13]},
|
|
{TELEPORT_EFFECT + 13, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[14]},
|
|
{TELEPORT_EFFECT + 14, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[15]},
|
|
{TELEPORT_EFFECT + 15, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[16]},
|
|
{TELEPORT_EFFECT + 16, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[17]},
|
|
{TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect[18]},
|
|
{TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, DoSuicide, &s_TeleportEffect[18]},
|
|
};
|
|
|
|
// Spawn a RIPPER teleport effect
|
|
ANIMATOR DoTeleRipper;
|
|
STATE s_TeleportEffect2[] =
|
|
{
|
|
{TELEPORT_EFFECT + 0, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[1]},
|
|
{TELEPORT_EFFECT + 1, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[2]},
|
|
{TELEPORT_EFFECT + 2, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[3]},
|
|
{TELEPORT_EFFECT + 3, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[4]},
|
|
{TELEPORT_EFFECT + 4, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[5]},
|
|
{TELEPORT_EFFECT + 5, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[6]},
|
|
{TELEPORT_EFFECT + 6, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[7]},
|
|
{TELEPORT_EFFECT + 7, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[8]},
|
|
{TELEPORT_EFFECT + 8, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[9]},
|
|
{TELEPORT_EFFECT + 9, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[10]},
|
|
{TELEPORT_EFFECT + 10, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[11]},
|
|
{TELEPORT_EFFECT + 11, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[12]},
|
|
{TELEPORT_EFFECT + 12, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[13]},
|
|
{TELEPORT_EFFECT + 13, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[14]},
|
|
{TELEPORT_EFFECT + 14, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[15]},
|
|
{TELEPORT_EFFECT + 15, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[16]},
|
|
{TELEPORT_EFFECT + 16, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[17]},
|
|
{TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, NullAnimator, &s_TeleportEffect2[18]},
|
|
{TELEPORT_EFFECT + 17, SF_QUICK_CALL, DoTeleRipper, &s_TeleportEffect2[19]},
|
|
{TELEPORT_EFFECT + 17, TELEPORT_EFFECT_RATE, DoSuicide, &s_TeleportEffect2[19]},
|
|
};
|
|
|
|
|
|
ANIMATOR DoElectro;
|
|
#define ELECTRO_SNAKE 2073
|
|
#define ELECTRO_PLAYER (ELECTRO)
|
|
#define ELECTRO_ENEMY (ELECTRO + 1)
|
|
#define ELECTRO_SHARD (ELECTRO + 2)
|
|
|
|
STATE s_Electro[] =
|
|
{
|
|
{ELECTRO + 0, 12, DoElectro, &s_Electro[1]},
|
|
{ELECTRO + 1, 12, DoElectro, &s_Electro[2]},
|
|
{ELECTRO + 2, 12, DoElectro, &s_Electro[3]},
|
|
{ELECTRO + 3, 12, DoElectro, &s_Electro[0]}
|
|
};
|
|
|
|
STATE s_ElectroShrap[] =
|
|
{
|
|
{ELECTRO + 0, 12, DoShrapDamage, &s_ElectroShrap[1]},
|
|
{ELECTRO + 1, 12, DoShrapDamage, &s_ElectroShrap[2]},
|
|
{ELECTRO + 2, 12, DoShrapDamage, &s_ElectroShrap[3]},
|
|
{ELECTRO + 3, 12, DoShrapDamage, &s_ElectroShrap[0]}
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// EXPS
|
|
//
|
|
//////////////////////
|
|
#define GRENADE_EXP 3121
|
|
#define MINE_EXP GRENADE_EXP+1
|
|
#define GRENADE_EXP_RATE 6
|
|
|
|
#if 0
|
|
STATE s_GrenadeExp[] =
|
|
{
|
|
{GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[1]},
|
|
{GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[2]},
|
|
{GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[3]},
|
|
{GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[4]},
|
|
{GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[5]},
|
|
{GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[6]},
|
|
{GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[7]},
|
|
{GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[8]},
|
|
{GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[9]},
|
|
{GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[10]},
|
|
{GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[11]},
|
|
{GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[12]},
|
|
{GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[13]},
|
|
{GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[14]},
|
|
{GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[15]},
|
|
{GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[16]},
|
|
{GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[17]},
|
|
{GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[18]},
|
|
{GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[19]},
|
|
{GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[20]},
|
|
{GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[21]},
|
|
{GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[22]},
|
|
{GRENADE_EXP + 21, 100, DoSuicide, &s_GrenadeExp[22]}
|
|
};
|
|
#else
|
|
STATE s_GrenadeSmallExp[] =
|
|
{
|
|
{GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[1]},
|
|
{GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[2]},
|
|
{GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[3]},
|
|
{GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[4]},
|
|
{GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[5]},
|
|
{GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[6]},
|
|
{GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[7]},
|
|
{GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[8]},
|
|
{GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[9]},
|
|
{GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[10]},
|
|
{GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[11]},
|
|
{GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[12]},
|
|
{GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[13]},
|
|
{GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[14]},
|
|
{GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[15]},
|
|
{GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[16]},
|
|
{GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[17]},
|
|
{GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[18]},
|
|
{GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[19]},
|
|
{GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[20]},
|
|
{GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[21]},
|
|
{GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeSmallExp[22]},
|
|
{GRENADE_EXP + 21, 100, DoSuicide, &s_GrenadeSmallExp[22]}
|
|
};
|
|
|
|
ANIMATOR SpawnGrenadeSmallExp;
|
|
STATE s_GrenadeExp[] =
|
|
{
|
|
{GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[1]},
|
|
{GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[2]},
|
|
{GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[3]},
|
|
{GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[4]},
|
|
{GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[5]},
|
|
{GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[6]},
|
|
{GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[7]},
|
|
{GRENADE_EXP + 6, SF_QUICK_CALL, SpawnGrenadeSmallExp, &s_GrenadeExp[8]},
|
|
{GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[9]},
|
|
{GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[10]},
|
|
{GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[11]},
|
|
{GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[12]},
|
|
{GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[13]},
|
|
{GRENADE_EXP + 12, SF_QUICK_CALL, SpawnGrenadeSmallExp, &s_GrenadeExp[14]},
|
|
{GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[15]},
|
|
{GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[16]},
|
|
{GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[17]},
|
|
{GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[18]},
|
|
{GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[19]},
|
|
{GRENADE_EXP + 17, SF_QUICK_CALL, SpawnGrenadeSmallExp, &s_GrenadeExp[20]},
|
|
{GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[21]},
|
|
{GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[22]},
|
|
{GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[23]},
|
|
{GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[24]},
|
|
{GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_GrenadeExp[25]},
|
|
{GRENADE_EXP + 21, 100, DoSuicide, &s_GrenadeExp[25]}
|
|
};
|
|
#endif
|
|
|
|
#define MINE_EXP GRENADE_EXP+1
|
|
ANIMATOR DoMineExp, DoMineExpMine;
|
|
STATE s_MineExp[] =
|
|
{
|
|
{GRENADE_EXP + 0, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[1]},
|
|
{GRENADE_EXP + 1, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[2]},
|
|
{GRENADE_EXP + 2, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[3]},
|
|
{GRENADE_EXP + 3, 0|SF_QUICK_CALL, DoMineExp, &s_MineExp[4]},
|
|
{GRENADE_EXP + 3, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[5]},
|
|
{GRENADE_EXP + 4, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[6]},
|
|
{GRENADE_EXP + 5, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[7]},
|
|
{GRENADE_EXP + 6, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[8]},
|
|
{GRENADE_EXP + 7, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[9]},
|
|
{GRENADE_EXP + 8, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[10]},
|
|
{GRENADE_EXP + 9, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[11]},
|
|
{GRENADE_EXP + 10, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[12]},
|
|
{GRENADE_EXP + 11, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[13]},
|
|
{GRENADE_EXP + 12, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[14]},
|
|
{GRENADE_EXP + 13, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[15]},
|
|
{GRENADE_EXP + 14, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[16]},
|
|
{GRENADE_EXP + 15, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[17]},
|
|
{GRENADE_EXP + 16, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[18]},
|
|
{GRENADE_EXP + 17, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[19]},
|
|
{GRENADE_EXP + 17, 0|SF_QUICK_CALL, DoMineExpMine, &s_MineExp[20]},
|
|
{GRENADE_EXP + 18, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[21]},
|
|
{GRENADE_EXP + 19, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[22]},
|
|
{GRENADE_EXP + 20, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[23]},
|
|
{GRENADE_EXP + 21, GRENADE_EXP_RATE, NullAnimator, &s_MineExp[24]},
|
|
{GRENADE_EXP + 21, 100, DoSuicide, &s_MineExp[24]}
|
|
};
|
|
|
|
#define EXP_RATE 7
|
|
#define BOLT_EXP EXP
|
|
#define FIREBALL_EXP EXP+1
|
|
#define BASIC_EXP EXP+2
|
|
#define SECTOR_EXP EXP+3
|
|
#define MICRO_EXP EXP+5
|
|
#define TRACER_EXP EXP+6
|
|
#define TANK_SHELL_EXP EXP+7
|
|
|
|
STATE s_BasicExp[] =
|
|
{
|
|
{EXP + 0, EXP_RATE, NullAnimator, &s_BasicExp[1]},
|
|
{EXP + 1, EXP_RATE, NullAnimator, &s_BasicExp[2]},
|
|
{EXP + 2, EXP_RATE, NullAnimator, &s_BasicExp[3]},
|
|
{EXP + 3, EXP_RATE, NullAnimator, &s_BasicExp[4]},
|
|
{EXP + 4, EXP_RATE, NullAnimator, &s_BasicExp[5]},
|
|
{EXP + 5, EXP_RATE, NullAnimator, &s_BasicExp[6]},
|
|
{EXP + 6, EXP_RATE, NullAnimator, &s_BasicExp[7]},
|
|
{EXP + 7, EXP_RATE, NullAnimator, &s_BasicExp[8]},
|
|
{EXP + 8, EXP_RATE, NullAnimator, &s_BasicExp[9]},
|
|
{EXP + 9, EXP_RATE, NullAnimator, &s_BasicExp[10]},
|
|
{EXP + 10, EXP_RATE, NullAnimator, &s_BasicExp[11]},
|
|
{EXP + 11, EXP_RATE, NullAnimator, &s_BasicExp[12]},
|
|
{EXP + 12, EXP_RATE, NullAnimator, &s_BasicExp[13]},
|
|
{EXP + 13, EXP_RATE, NullAnimator, &s_BasicExp[14]},
|
|
{EXP + 14, EXP_RATE, NullAnimator, &s_BasicExp[15]},
|
|
{EXP + 15, EXP_RATE, NullAnimator, &s_BasicExp[16]},
|
|
{EXP + 16, EXP_RATE, NullAnimator, &s_BasicExp[17]},
|
|
{EXP + 17, EXP_RATE, NullAnimator, &s_BasicExp[18]},
|
|
{EXP + 18, EXP_RATE, NullAnimator, &s_BasicExp[19]},
|
|
{EXP + 19, EXP_RATE, NullAnimator, &s_BasicExp[20]},
|
|
{EXP + 20, 100, DoSuicide, &s_BasicExp[0]}
|
|
};
|
|
|
|
#define MICRO_EXP_RATE 3
|
|
ANIMATOR DoExpDamageTest;
|
|
|
|
STATE s_MicroExp[] =
|
|
{
|
|
{EXP + 0, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[1]},
|
|
{EXP + 0, SF_QUICK_CALL, DoExpDamageTest, &s_MicroExp[2]},
|
|
{EXP + 1, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[3]},
|
|
{EXP + 2, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[4]},
|
|
{EXP + 3, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[5]},
|
|
{EXP + 4, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[6]},
|
|
{EXP + 5, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[7]},
|
|
{EXP + 6, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[8]},
|
|
{EXP + 7, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[9]},
|
|
{EXP + 8, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[10]},
|
|
{EXP + 9, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[11]},
|
|
{EXP + 10, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[12]},
|
|
{EXP + 11, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[13]},
|
|
{EXP + 12, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[14]},
|
|
{EXP + 13, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[15]},
|
|
{EXP + 14, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[16]},
|
|
{EXP + 15, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[17]},
|
|
{EXP + 16, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[18]},
|
|
{EXP + 17, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[19]},
|
|
{EXP + 18, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[20]},
|
|
{EXP + 19, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[21]},
|
|
{EXP + 20, MICRO_EXP_RATE, NullAnimator, &s_MicroExp[22]},
|
|
{EXP + 20, 100, DoSuicide, &s_MicroExp[22]}
|
|
};
|
|
|
|
|
|
#define BIG_GUN_FLAME_RATE 15
|
|
STATE s_BigGunFlame[] =
|
|
{
|
|
// first 3 frames
|
|
{EXP + 0, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[1]},
|
|
{EXP + 1, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[2]},
|
|
{EXP + 2, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[3]},
|
|
// last 4 frames frames
|
|
{EXP + 17, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[4]},
|
|
{EXP + 18, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[5]},
|
|
{EXP + 19, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[6]},
|
|
{EXP + 20, BIG_GUN_FLAME_RATE, NullAnimator, &s_BigGunFlame[7]},
|
|
{EXP + 20, 100, DoSuicide, &s_BigGunFlame[0]}
|
|
};
|
|
|
|
STATE s_BoltExp[] =
|
|
{
|
|
{EXP + 0, EXP_RATE, NullAnimator, &s_BoltExp[1]},
|
|
{EXP + 0, SF_QUICK_CALL, NullAnimator, &s_BoltExp[2]},
|
|
{EXP + 0, SF_QUICK_CALL, SpawnShrapX, &s_BoltExp[3]},
|
|
{EXP + 1, EXP_RATE, NullAnimator, &s_BoltExp[4]},
|
|
{EXP + 2, EXP_RATE, NullAnimator, &s_BoltExp[5]},
|
|
{EXP + 3, EXP_RATE, NullAnimator, &s_BoltExp[6]},
|
|
{EXP + 4, EXP_RATE, NullAnimator, &s_BoltExp[7]},
|
|
{EXP + 5, EXP_RATE, NullAnimator, &s_BoltExp[8]},
|
|
{EXP + 6, EXP_RATE, NullAnimator, &s_BoltExp[9]},
|
|
{EXP + 7, EXP_RATE, NullAnimator, &s_BoltExp[10]},
|
|
{EXP + 7, SF_QUICK_CALL, SpawnShrapX, &s_BoltExp[11]},
|
|
{EXP + 8, EXP_RATE, NullAnimator, &s_BoltExp[12]},
|
|
{EXP + 9, EXP_RATE, NullAnimator, &s_BoltExp[13]},
|
|
{EXP + 10, EXP_RATE, NullAnimator, &s_BoltExp[14]},
|
|
{EXP + 11, EXP_RATE, NullAnimator, &s_BoltExp[15]},
|
|
{EXP + 12, EXP_RATE, NullAnimator, &s_BoltExp[16]},
|
|
{EXP + 13, EXP_RATE, NullAnimator, &s_BoltExp[17]},
|
|
{EXP + 14, EXP_RATE, NullAnimator, &s_BoltExp[18]},
|
|
{EXP + 15, EXP_RATE, NullAnimator, &s_BoltExp[19]},
|
|
{EXP + 16, EXP_RATE, NullAnimator, &s_BoltExp[20]},
|
|
{EXP + 17, EXP_RATE, NullAnimator, &s_BoltExp[21]},
|
|
{EXP + 18, EXP_RATE, NullAnimator, &s_BoltExp[22]},
|
|
{EXP + 19, EXP_RATE, NullAnimator, &s_BoltExp[23]},
|
|
{EXP + 20, EXP_RATE, NullAnimator, &s_BoltExp[24]},
|
|
{EXP + 20, 100, DoSuicide, &s_BoltExp[0]}
|
|
};
|
|
|
|
STATE s_TankShellExp[] =
|
|
{
|
|
{EXP + 0, EXP_RATE, NullAnimator, &s_TankShellExp[1]},
|
|
{EXP + 0, SF_QUICK_CALL, NullAnimator, &s_TankShellExp[2]},
|
|
{EXP + 0, SF_QUICK_CALL, SpawnShrapX, &s_TankShellExp[3]},
|
|
{EXP + 1, EXP_RATE, NullAnimator, &s_TankShellExp[4]},
|
|
{EXP + 2, EXP_RATE, NullAnimator, &s_TankShellExp[5]},
|
|
{EXP + 3, EXP_RATE, NullAnimator, &s_TankShellExp[6]},
|
|
{EXP + 4, EXP_RATE, NullAnimator, &s_TankShellExp[7]},
|
|
{EXP + 5, EXP_RATE, NullAnimator, &s_TankShellExp[8]},
|
|
{EXP + 6, EXP_RATE, NullAnimator, &s_TankShellExp[9]},
|
|
{EXP + 7, EXP_RATE, NullAnimator, &s_TankShellExp[10]},
|
|
{EXP + 7, SF_QUICK_CALL, SpawnShrapX, &s_TankShellExp[11]},
|
|
{EXP + 8, EXP_RATE, NullAnimator, &s_TankShellExp[12]},
|
|
{EXP + 9, EXP_RATE, NullAnimator, &s_TankShellExp[13]},
|
|
{EXP + 10, EXP_RATE, NullAnimator, &s_TankShellExp[14]},
|
|
{EXP + 11, EXP_RATE, NullAnimator, &s_TankShellExp[15]},
|
|
{EXP + 12, EXP_RATE, NullAnimator, &s_TankShellExp[16]},
|
|
{EXP + 13, EXP_RATE, NullAnimator, &s_TankShellExp[17]},
|
|
{EXP + 14, EXP_RATE, NullAnimator, &s_TankShellExp[18]},
|
|
{EXP + 15, EXP_RATE, NullAnimator, &s_TankShellExp[19]},
|
|
{EXP + 16, EXP_RATE, NullAnimator, &s_TankShellExp[20]},
|
|
{EXP + 17, EXP_RATE, NullAnimator, &s_TankShellExp[21]},
|
|
{EXP + 18, EXP_RATE, NullAnimator, &s_TankShellExp[22]},
|
|
{EXP + 19, EXP_RATE, NullAnimator, &s_TankShellExp[23]},
|
|
{EXP + 20, EXP_RATE, NullAnimator, &s_TankShellExp[24]},
|
|
{EXP + 20, 100, DoSuicide, &s_TankShellExp[0]}
|
|
};
|
|
|
|
#define TRACER_EXP_RATE 4
|
|
STATE s_TracerExp[] =
|
|
{
|
|
{EXP + 0, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[1]},
|
|
{EXP + 0, SF_QUICK_CALL, NullAnimator, &s_TracerExp[2]},
|
|
{EXP + 0, SF_QUICK_CALL, NullAnimator, &s_TracerExp[3]},
|
|
{EXP + 1, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[4]},
|
|
{EXP + 2, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[5]},
|
|
{EXP + 3, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[6]},
|
|
{EXP + 4, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[7]},
|
|
{EXP + 5, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[8]},
|
|
{EXP + 6, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[9]},
|
|
{EXP + 7, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[10]},
|
|
{EXP + 7, SF_QUICK_CALL, NullAnimator, &s_TracerExp[11]},
|
|
{EXP + 8, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[12]},
|
|
{EXP + 9, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[13]},
|
|
{EXP + 10, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[14]},
|
|
{EXP + 11, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[15]},
|
|
{EXP + 12, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[16]},
|
|
{EXP + 13, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[17]},
|
|
{EXP + 14, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[18]},
|
|
{EXP + 15, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[19]},
|
|
{EXP + 16, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[20]},
|
|
{EXP + 17, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[21]},
|
|
{EXP + 18, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[22]},
|
|
{EXP + 19, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[23]},
|
|
{EXP + 20, TRACER_EXP_RATE, NullAnimator, &s_TracerExp[24]},
|
|
{EXP + 20, 100, DoSuicide, &s_TracerExp[0]}
|
|
};
|
|
|
|
#define EXP_RATE 7
|
|
ANIMATOR SpawnShrapX;
|
|
ANIMATOR DoSectorExp;
|
|
|
|
STATE s_SectorExp[] =
|
|
{
|
|
{EXP + 0, EXP_RATE, DoSectorExp, &s_SectorExp[1]},
|
|
{EXP + 0, SF_QUICK_CALL, SpawnShrapX, &s_SectorExp[2]},
|
|
{EXP + 0, SF_QUICK_CALL, DoSectorExp, &s_SectorExp[3]},
|
|
{EXP + 1, EXP_RATE, DoSectorExp, &s_SectorExp[4]},
|
|
{EXP + 2, EXP_RATE, DoSectorExp, &s_SectorExp[5]},
|
|
{EXP + 3, EXP_RATE, DoSectorExp, &s_SectorExp[6]},
|
|
{EXP + 4, EXP_RATE, DoSectorExp, &s_SectorExp[7]},
|
|
{EXP + 5, EXP_RATE, DoSectorExp, &s_SectorExp[8]},
|
|
{EXP + 6, EXP_RATE, DoSectorExp, &s_SectorExp[9]},
|
|
{EXP + 7, EXP_RATE, DoSectorExp, &s_SectorExp[10]},
|
|
{EXP + 7, SF_QUICK_CALL, DoSectorExp, &s_SectorExp[11]},
|
|
{EXP + 8, EXP_RATE, DoSectorExp, &s_SectorExp[12]},
|
|
{EXP + 9, EXP_RATE, DoSectorExp, &s_SectorExp[13]},
|
|
{EXP + 10, EXP_RATE, DoSectorExp, &s_SectorExp[14]},
|
|
{EXP + 11, EXP_RATE, DoSectorExp, &s_SectorExp[15]},
|
|
{EXP + 12, EXP_RATE, DoSectorExp, &s_SectorExp[16]},
|
|
{EXP + 13, EXP_RATE, DoSectorExp, &s_SectorExp[17]},
|
|
{EXP + 14, EXP_RATE, DoSectorExp, &s_SectorExp[18]},
|
|
{EXP + 15, EXP_RATE, DoSectorExp, &s_SectorExp[19]},
|
|
{EXP + 16, EXP_RATE, DoSectorExp, &s_SectorExp[20]},
|
|
{EXP + 17, EXP_RATE, DoSectorExp, &s_SectorExp[21]},
|
|
{EXP + 18, EXP_RATE, DoSectorExp, &s_SectorExp[22]},
|
|
{EXP + 19, EXP_RATE, DoSectorExp, &s_SectorExp[23]},
|
|
{EXP + 20, EXP_RATE, DoSectorExp, &s_SectorExp[24]},
|
|
{EXP + 20, 100, DoSuicide, &s_SectorExp[0]}
|
|
};
|
|
|
|
|
|
#define FIREBALL_DISS 3196
|
|
#define FIREBALL_DISS_RATE 8
|
|
STATE s_FireballExp[] =
|
|
{
|
|
{FIREBALL_DISS + 0, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[1]},
|
|
{FIREBALL_DISS + 1, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[2]},
|
|
{FIREBALL_DISS + 2, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[3]},
|
|
{FIREBALL_DISS + 3, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[4]},
|
|
{FIREBALL_DISS + 4, FIREBALL_DISS_RATE, NullAnimator, &s_FireballExp[5]},
|
|
{FIREBALL_DISS + 4, 100, DoSuicide, &s_FireballExp[0]}
|
|
};
|
|
|
|
#define NAP_EXP (3072)
|
|
#define NAP_EXP_RATE 6
|
|
|
|
STATE s_NapExp[] =
|
|
{
|
|
{NAP_EXP + 0, NAP_EXP_RATE, NullAnimator, &s_NapExp[1]},
|
|
{NAP_EXP + 0, 0 | SF_QUICK_CALL, DoDamageTest, &s_NapExp[2]},
|
|
{NAP_EXP + 1, NAP_EXP_RATE, NullAnimator, &s_NapExp[3]},
|
|
{NAP_EXP + 2, NAP_EXP_RATE, NullAnimator, &s_NapExp[4]},
|
|
{NAP_EXP + 3, NAP_EXP_RATE, NullAnimator, &s_NapExp[5]},
|
|
{NAP_EXP + 4, NAP_EXP_RATE, NullAnimator, &s_NapExp[6]},
|
|
{NAP_EXP + 5, NAP_EXP_RATE, NullAnimator, &s_NapExp[7]},
|
|
{NAP_EXP + 6, NAP_EXP_RATE, NullAnimator, &s_NapExp[8]},
|
|
{NAP_EXP + 7, NAP_EXP_RATE, NullAnimator, &s_NapExp[9]},
|
|
{NAP_EXP + 8, NAP_EXP_RATE, NullAnimator, &s_NapExp[10]},
|
|
{NAP_EXP + 9, NAP_EXP_RATE, NullAnimator, &s_NapExp[11]},
|
|
{NAP_EXP + 10, NAP_EXP_RATE, NullAnimator, &s_NapExp[12]},
|
|
{NAP_EXP + 11, NAP_EXP_RATE, NullAnimator, &s_NapExp[13]},
|
|
{NAP_EXP + 12, NAP_EXP_RATE, NullAnimator, &s_NapExp[14]},
|
|
{NAP_EXP + 13, NAP_EXP_RATE, NullAnimator, &s_NapExp[15]},
|
|
{NAP_EXP + 14, NAP_EXP_RATE, NullAnimator, &s_NapExp[16]},
|
|
{NAP_EXP + 15, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[17]},
|
|
{NAP_EXP + 16, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[18]},
|
|
{NAP_EXP + 17, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[19]},
|
|
{NAP_EXP + 18, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[20]},
|
|
{NAP_EXP + 19, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[21]},
|
|
{NAP_EXP + 20, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[22]},
|
|
{NAP_EXP + 21, NAP_EXP_RATE-2, NullAnimator, &s_NapExp[23]},
|
|
{NAP_EXP + 21, NAP_EXP_RATE-2, DoSuicide, &s_NapExp[23]}
|
|
};
|
|
|
|
ANIMATOR DoFireballFlames;
|
|
#define FLAME_RATE 6
|
|
|
|
STATE s_FireballFlames[] =
|
|
{
|
|
{FIREBALL_FLAMES + 0, FLAME_RATE, DoFireballFlames, &s_FireballFlames[1]},
|
|
{FIREBALL_FLAMES + 1, FLAME_RATE, DoFireballFlames, &s_FireballFlames[2]},
|
|
{FIREBALL_FLAMES + 2, FLAME_RATE, DoFireballFlames, &s_FireballFlames[3]},
|
|
{FIREBALL_FLAMES + 3, FLAME_RATE, DoFireballFlames, &s_FireballFlames[4]},
|
|
{FIREBALL_FLAMES + 4, FLAME_RATE, DoFireballFlames, &s_FireballFlames[5]},
|
|
{FIREBALL_FLAMES + 5, FLAME_RATE, DoFireballFlames, &s_FireballFlames[6]},
|
|
{FIREBALL_FLAMES + 6, FLAME_RATE, DoFireballFlames, &s_FireballFlames[7]},
|
|
{FIREBALL_FLAMES + 7, FLAME_RATE, DoFireballFlames, &s_FireballFlames[8]},
|
|
{FIREBALL_FLAMES + 8, FLAME_RATE, DoFireballFlames, &s_FireballFlames[9]},
|
|
{FIREBALL_FLAMES + 9, FLAME_RATE, DoFireballFlames, &s_FireballFlames[10]},
|
|
{FIREBALL_FLAMES +10, FLAME_RATE, DoFireballFlames, &s_FireballFlames[11]},
|
|
{FIREBALL_FLAMES +11, FLAME_RATE, DoFireballFlames, &s_FireballFlames[12]},
|
|
{FIREBALL_FLAMES +12, FLAME_RATE, DoFireballFlames, &s_FireballFlames[13]},
|
|
{FIREBALL_FLAMES +13, FLAME_RATE, DoFireballFlames, &s_FireballFlames[0]},
|
|
};
|
|
|
|
ANIMATOR DoBreakFlames;
|
|
#define FLAME_RATE 6
|
|
|
|
STATE s_BreakFlames[] =
|
|
{
|
|
{FIREBALL_FLAMES + 0, FLAME_RATE, DoBreakFlames, &s_BreakFlames[1]},
|
|
{FIREBALL_FLAMES + 1, FLAME_RATE, DoBreakFlames, &s_BreakFlames[2]},
|
|
{FIREBALL_FLAMES + 2, FLAME_RATE, DoBreakFlames, &s_BreakFlames[3]},
|
|
{FIREBALL_FLAMES + 3, FLAME_RATE, DoBreakFlames, &s_BreakFlames[4]},
|
|
{FIREBALL_FLAMES + 4, FLAME_RATE, DoBreakFlames, &s_BreakFlames[5]},
|
|
{FIREBALL_FLAMES + 5, FLAME_RATE, DoBreakFlames, &s_BreakFlames[6]},
|
|
{FIREBALL_FLAMES + 6, FLAME_RATE, DoBreakFlames, &s_BreakFlames[7]},
|
|
{FIREBALL_FLAMES + 7, FLAME_RATE, DoBreakFlames, &s_BreakFlames[8]},
|
|
{FIREBALL_FLAMES + 8, FLAME_RATE, DoBreakFlames, &s_BreakFlames[9]},
|
|
{FIREBALL_FLAMES + 9, FLAME_RATE, DoBreakFlames, &s_BreakFlames[10]},
|
|
{FIREBALL_FLAMES +10, FLAME_RATE, DoBreakFlames, &s_BreakFlames[11]},
|
|
{FIREBALL_FLAMES +11, FLAME_RATE, DoBreakFlames, &s_BreakFlames[12]},
|
|
{FIREBALL_FLAMES +12, FLAME_RATE, DoBreakFlames, &s_BreakFlames[13]},
|
|
{FIREBALL_FLAMES +13, FLAME_RATE, DoBreakFlames, &s_BreakFlames[0]},
|
|
};
|
|
|
|
//////////////////////
|
|
//
|
|
// FIREBALL
|
|
//
|
|
//////////////////////
|
|
|
|
#if 1
|
|
ANIMATOR DoFireball;
|
|
#define FIREBALL_RATE 8
|
|
#define GORO_FIREBALL FIREBALL+1
|
|
|
|
STATE s_Fireball[] =
|
|
{
|
|
{FIREBALL + 0, 12, DoFireball, &s_Fireball[1]},
|
|
{FIREBALL + 1, 12, DoFireball, &s_Fireball[2]},
|
|
{FIREBALL + 2, 12, DoFireball, &s_Fireball[3]},
|
|
{FIREBALL + 3, 12, DoFireball, &s_Fireball[0]}
|
|
};
|
|
|
|
#else
|
|
#define GORO_FIREBALL FIREBALL_R0
|
|
#define FIREBALL_RATE 6
|
|
ANIMATOR DoFireball;
|
|
|
|
STATE s_Fireball[5][4] =
|
|
{
|
|
{
|
|
{FIREBALL_R0 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[0][1]},
|
|
{FIREBALL_R0 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[0][2]},
|
|
{FIREBALL_R0 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[0][3]},
|
|
{FIREBALL_R0 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[0][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R1 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[1][1]},
|
|
{FIREBALL_R1 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[1][2]},
|
|
{FIREBALL_R1 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[1][3]},
|
|
{FIREBALL_R1 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[1][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R2 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[2][1]},
|
|
{FIREBALL_R2 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[2][2]},
|
|
{FIREBALL_R2 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[2][3]},
|
|
{FIREBALL_R2 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[2][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R3 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[3][1]},
|
|
{FIREBALL_R3 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[3][2]},
|
|
{FIREBALL_R3 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[3][3]},
|
|
{FIREBALL_R3 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[3][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R4 + 0, FIREBALL_RATE, DoFireball, &s_Fireball[4][1]},
|
|
{FIREBALL_R4 + 1, FIREBALL_RATE, DoFireball, &s_Fireball[4][2]},
|
|
{FIREBALL_R4 + 2, FIREBALL_RATE, DoFireball, &s_Fireball[4][3]},
|
|
{FIREBALL_R4 + 3, FIREBALL_RATE, DoFireball, &s_Fireball[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_Fireball[] =
|
|
{
|
|
s_Fireball[0],
|
|
s_Fireball[1],
|
|
s_Fireball[2],
|
|
s_Fireball[3],
|
|
s_Fireball[4]
|
|
};
|
|
#endif
|
|
|
|
#if 0
|
|
ANIMATOR DoRing;
|
|
|
|
STATE s_Ring[5][4] =
|
|
{
|
|
{
|
|
{FIREBALL_R0 + 0, FIREBALL_RATE, DoRing, &s_Ring[0][1]},
|
|
{FIREBALL_R0 + 1, FIREBALL_RATE, DoRing, &s_Ring[0][2]},
|
|
{FIREBALL_R0 + 2, FIREBALL_RATE, DoRing, &s_Ring[0][3]},
|
|
{FIREBALL_R0 + 3, FIREBALL_RATE, DoRing, &s_Ring[0][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R1 + 0, FIREBALL_RATE, DoRing, &s_Ring[1][1]},
|
|
{FIREBALL_R1 + 1, FIREBALL_RATE, DoRing, &s_Ring[1][2]},
|
|
{FIREBALL_R1 + 2, FIREBALL_RATE, DoRing, &s_Ring[1][3]},
|
|
{FIREBALL_R1 + 3, FIREBALL_RATE, DoRing, &s_Ring[1][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R2 + 0, FIREBALL_RATE, DoRing, &s_Ring[2][1]},
|
|
{FIREBALL_R2 + 1, FIREBALL_RATE, DoRing, &s_Ring[2][2]},
|
|
{FIREBALL_R2 + 2, FIREBALL_RATE, DoRing, &s_Ring[2][3]},
|
|
{FIREBALL_R2 + 3, FIREBALL_RATE, DoRing, &s_Ring[2][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R3 + 0, FIREBALL_RATE, DoRing, &s_Ring[3][1]},
|
|
{FIREBALL_R3 + 1, FIREBALL_RATE, DoRing, &s_Ring[3][2]},
|
|
{FIREBALL_R3 + 2, FIREBALL_RATE, DoRing, &s_Ring[3][3]},
|
|
{FIREBALL_R3 + 3, FIREBALL_RATE, DoRing, &s_Ring[3][0]},
|
|
},
|
|
{
|
|
{FIREBALL_R4 + 0, FIREBALL_RATE, DoRing, &s_Ring[4][1]},
|
|
{FIREBALL_R4 + 1, FIREBALL_RATE, DoRing, &s_Ring[4][2]},
|
|
{FIREBALL_R4 + 2, FIREBALL_RATE, DoRing, &s_Ring[4][3]},
|
|
{FIREBALL_R4 + 3, FIREBALL_RATE, DoRing, &s_Ring[4][0]},
|
|
}
|
|
};
|
|
|
|
STATEp sg_Ring[] =
|
|
{
|
|
s_Ring[0],
|
|
s_Ring[1],
|
|
s_Ring[2],
|
|
s_Ring[3],
|
|
s_Ring[4]
|
|
};
|
|
#else
|
|
ANIMATOR DoRing;
|
|
|
|
STATE s_Ring[] =
|
|
{
|
|
{FIREBALL + 0, 12, DoRing, &s_Ring[1]},
|
|
{FIREBALL + 1, 12, DoRing, &s_Ring[2]},
|
|
{FIREBALL + 2, 12, DoRing, &s_Ring[3]},
|
|
{FIREBALL + 3, 12, DoRing, &s_Ring[0]}
|
|
};
|
|
#endif
|
|
|
|
STATE s_Ring2[] =
|
|
{
|
|
{2031 + 0, 12, DoRing, &s_Ring2[1]},
|
|
{2031 + 1, 12, DoRing, &s_Ring2[2]},
|
|
{2031 + 2, 12, DoRing, &s_Ring2[3]},
|
|
{2031 + 3, 12, DoRing, &s_Ring2[0]}
|
|
};
|
|
|
|
ANIMATOR DoNapalm;
|
|
|
|
STATE s_Napalm[] =
|
|
{
|
|
{FIREBALL + 0, 12, DoNapalm, &s_Napalm[1]},
|
|
{FIREBALL + 1, 12, DoNapalm, &s_Napalm[2]},
|
|
{FIREBALL + 2, 12, DoNapalm, &s_Napalm[3]},
|
|
{FIREBALL + 3, 12, DoNapalm, &s_Napalm[0]}
|
|
};
|
|
|
|
|
|
ANIMATOR DoBloodWorm;
|
|
|
|
#if 1
|
|
#define BLOOD_WORM 2106
|
|
STATE s_BloodWorm[] =
|
|
{
|
|
{BLOOD_WORM + 0, 12, DoBloodWorm, &s_BloodWorm[1]},
|
|
{BLOOD_WORM + 1, 12, DoBloodWorm, &s_BloodWorm[2]},
|
|
{BLOOD_WORM + 2, 12, DoBloodWorm, &s_BloodWorm[3]},
|
|
{BLOOD_WORM + 3, 12, DoBloodWorm, &s_BloodWorm[4]},
|
|
{BLOOD_WORM + 2, 12, DoBloodWorm, &s_BloodWorm[5]},
|
|
{BLOOD_WORM + 1, 12, DoBloodWorm, &s_BloodWorm[0]}
|
|
};
|
|
#else
|
|
#define BLOOD_WORM FIREBALL+5
|
|
STATE s_BloodWorm[] =
|
|
{
|
|
{FIREBALL + 0, 12, DoBloodWorm, &s_BloodWorm[1]},
|
|
{FIREBALL + 1, 12, DoBloodWorm, &s_BloodWorm[2]},
|
|
{FIREBALL + 2, 12, DoBloodWorm, &s_BloodWorm[3]},
|
|
{FIREBALL + 3, 12, DoBloodWorm, &s_BloodWorm[0]}
|
|
};
|
|
#endif
|
|
|
|
#if 1
|
|
#define PLASMA_EXP BLOOD_WORM+1
|
|
#define PLASMA_EXP_RATE 8
|
|
|
|
STATE s_PlasmaExp[] =
|
|
{
|
|
{BLOOD_WORM + 0, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[1]},
|
|
{BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[2]},
|
|
{BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[3]},
|
|
{BLOOD_WORM + 3, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[4]},
|
|
{BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[5]},
|
|
{BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[6]},
|
|
{BLOOD_WORM + 0, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[7]},
|
|
{BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[8]},
|
|
{BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[9]},
|
|
{BLOOD_WORM + 3, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[10]},
|
|
{BLOOD_WORM + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[11]},
|
|
{BLOOD_WORM + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[12]},
|
|
{BLOOD_WORM + 0, PLASMA_EXP_RATE, DoSuicide, &s_PlasmaExp[12]},
|
|
};
|
|
|
|
#else
|
|
#define PLASMA_EXP (NAP_EXP+1)
|
|
#define PLASMA_EXP_RATE 4
|
|
|
|
STATE s_PlasmaExp[] =
|
|
{
|
|
{PLASMA_EXP + 0, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[1]},
|
|
{PLASMA_EXP + 0, 0 | SF_QUICK_CALL, DoDamageTest, &s_PlasmaExp[2]},
|
|
{PLASMA_EXP + 1, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[3]},
|
|
{PLASMA_EXP + 2, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[4]},
|
|
{PLASMA_EXP + 3, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[5]},
|
|
{PLASMA_EXP + 4, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[6]},
|
|
{PLASMA_EXP + 5, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[7]},
|
|
{PLASMA_EXP + 6, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[8]},
|
|
{PLASMA_EXP + 7, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[9]},
|
|
{PLASMA_EXP + 8, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[10]},
|
|
{PLASMA_EXP + 9, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[11]},
|
|
{PLASMA_EXP + 10, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[12]},
|
|
{PLASMA_EXP + 11, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[13]},
|
|
{PLASMA_EXP + 9, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[14]},
|
|
{PLASMA_EXP + 8, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[15]},
|
|
{PLASMA_EXP + 7, PLASMA_EXP_RATE, NullAnimator, &s_PlasmaExp[16]},
|
|
{PLASMA_EXP + 6, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[17]},
|
|
{PLASMA_EXP + 5, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[18]},
|
|
{PLASMA_EXP + 4, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[19]},
|
|
{PLASMA_EXP + 3, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[20]},
|
|
{PLASMA_EXP + 2, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[21]},
|
|
{PLASMA_EXP + 1, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[22]},
|
|
{PLASMA_EXP + 1, PLASMA_EXP_RATE-2, NullAnimator, &s_PlasmaExp[23]},
|
|
{PLASMA_EXP + 1, PLASMA_EXP_RATE-2, DoSuicide, &s_PlasmaExp[23]}
|
|
};
|
|
#endif
|
|
|
|
|
|
ANIMATOR DoMirv;
|
|
|
|
STATE s_Mirv[] =
|
|
{
|
|
{FIREBALL + 0, 12, DoMirv, &s_Mirv[1]},
|
|
{FIREBALL + 1, 12, DoMirv, &s_Mirv[2]},
|
|
{FIREBALL + 2, 12, DoMirv, &s_Mirv[3]},
|
|
{FIREBALL + 3, 12, DoMirv, &s_Mirv[0]}
|
|
};
|
|
|
|
ANIMATOR DoMirvMissile;
|
|
|
|
STATE s_MirvMissile[] =
|
|
{
|
|
{FIREBALL + 0, 12, DoMirvMissile, &s_MirvMissile[1]},
|
|
{FIREBALL + 1, 12, DoMirvMissile, &s_MirvMissile[2]},
|
|
{FIREBALL + 2, 12, DoMirvMissile, &s_MirvMissile[3]},
|
|
{FIREBALL + 3, 12, DoMirvMissile, &s_MirvMissile[0]}
|
|
};
|
|
|
|
//#define Vomit1 1740
|
|
//#define Vomit2 1741
|
|
#define Vomit1 1719
|
|
#define Vomit2 1721
|
|
//#define VomitSplash 1742
|
|
#define VomitSplash 1711
|
|
#define Vomit_RATE 16
|
|
ANIMATOR DoVomit,DoVomitSplash;
|
|
|
|
STATE s_Vomit1[] =
|
|
{
|
|
{Vomit1 + 0, Vomit_RATE, DoVomit, &s_Vomit1[0]}
|
|
};
|
|
|
|
STATE s_Vomit2[] =
|
|
{
|
|
{Vomit2 + 0, Vomit_RATE, DoVomit, &s_Vomit2[0]}
|
|
};
|
|
|
|
STATE s_VomitSplash[] =
|
|
{
|
|
{VomitSplash + 0, Vomit_RATE, DoVomitSplash, &s_VomitSplash[0]}
|
|
};
|
|
|
|
#define GORE_Head 1670
|
|
#define GORE_Head_RATE 16
|
|
|
|
STATE s_GoreHead[] =
|
|
{
|
|
{GORE_Head + 0, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[1]},
|
|
{GORE_Head + 1, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[2]},
|
|
{GORE_Head + 2, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[3]},
|
|
{GORE_Head + 3, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[4]},
|
|
{GORE_Head + 4, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[5]},
|
|
{GORE_Head + 5, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[6]},
|
|
{GORE_Head + 6, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[7]},
|
|
{GORE_Head + 7, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[8]},
|
|
{GORE_Head + 8, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[9]},
|
|
{GORE_Head + 9, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[10]},
|
|
{GORE_Head + 10, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[11]},
|
|
{GORE_Head + 11, GORE_Head_RATE, DoShrapJumpFall, &s_GoreHead[0]},
|
|
};
|
|
|
|
#define GORE_Leg 1689
|
|
#define GORE_Leg_RATE 16
|
|
|
|
STATE s_GoreLeg[] =
|
|
{
|
|
{GORE_Leg + 0, GORE_Leg_RATE, DoShrapJumpFall, &s_GoreLeg[1]},
|
|
{GORE_Leg + 1, GORE_Leg_RATE, DoShrapJumpFall, &s_GoreLeg[2]},
|
|
{GORE_Leg + 2, GORE_Leg_RATE, DoShrapJumpFall, &s_GoreLeg[0]},
|
|
};
|
|
|
|
#define GORE_Eye 1692
|
|
#define GORE_Eye_RATE 16
|
|
|
|
STATE s_GoreEye[] =
|
|
{
|
|
{GORE_Eye + 0, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[1]},
|
|
{GORE_Eye + 1, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[2]},
|
|
{GORE_Eye + 2, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[3]},
|
|
{GORE_Eye + 3, GORE_Eye_RATE, DoShrapJumpFall, &s_GoreEye[0]},
|
|
};
|
|
|
|
#define GORE_Torso 1696
|
|
#define GORE_Torso_RATE 16
|
|
|
|
STATE s_GoreTorso[] =
|
|
{
|
|
{GORE_Torso + 0, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[1]},
|
|
{GORE_Torso + 1, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[2]},
|
|
{GORE_Torso + 2, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[3]},
|
|
{GORE_Torso + 3, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[4]},
|
|
{GORE_Torso + 4, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[5]},
|
|
{GORE_Torso + 5, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[6]},
|
|
{GORE_Torso + 6, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[7]},
|
|
{GORE_Torso + 7, GORE_Torso_RATE, DoShrapJumpFall, &s_GoreTorso[0]},
|
|
};
|
|
|
|
|
|
#define GORE_Arm 1550
|
|
#define GORE_Arm_RATE 16
|
|
|
|
STATE s_GoreArm[] =
|
|
{
|
|
{GORE_Arm + 0, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[1]},
|
|
{GORE_Arm + 1, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[2]},
|
|
{GORE_Arm + 2, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[3]},
|
|
{GORE_Arm + 3, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[4]},
|
|
{GORE_Arm + 4, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[5]},
|
|
{GORE_Arm + 5, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[6]},
|
|
{GORE_Arm + 6, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[7]},
|
|
{GORE_Arm + 7, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[8]},
|
|
{GORE_Arm + 8, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[9]},
|
|
{GORE_Arm + 9, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[10]},
|
|
{GORE_Arm + 10, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[11]},
|
|
{GORE_Arm + 11, GORE_Arm_RATE, DoShrapJumpFall, &s_GoreArm[0]},
|
|
};
|
|
|
|
#define GORE_Lung 903
|
|
#define GORE_Lung_RATE 16
|
|
|
|
STATE s_GoreLung[] =
|
|
{
|
|
{GORE_Lung + 0, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[1]},
|
|
{GORE_Lung + 1, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[2]},
|
|
{GORE_Lung + 2, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[3]},
|
|
{GORE_Lung + 3, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[4]},
|
|
{GORE_Lung + 4, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[5]},
|
|
{GORE_Lung + 5, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[6]},
|
|
{GORE_Lung + 6, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[7]},
|
|
{GORE_Lung + 7, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[8]},
|
|
{GORE_Lung + 8, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[9]},
|
|
{GORE_Lung + 9, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[10]},
|
|
{GORE_Lung + 10, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[11]},
|
|
{GORE_Lung + 11, GORE_Lung_RATE, DoShrapJumpFall, &s_GoreLung[0]},
|
|
};
|
|
|
|
#define GORE_Liver 918
|
|
#define GORE_Liver_RATE 16
|
|
|
|
STATE s_GoreLiver[] =
|
|
{
|
|
{GORE_Liver + 0, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[1]},
|
|
{GORE_Liver + 1, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[2]},
|
|
{GORE_Liver + 2, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[3]},
|
|
{GORE_Liver + 3, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[4]},
|
|
{GORE_Liver + 4, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[5]},
|
|
{GORE_Liver + 5, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[6]},
|
|
{GORE_Liver + 6, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[7]},
|
|
{GORE_Liver + 7, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[8]},
|
|
{GORE_Liver + 8, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[9]},
|
|
{GORE_Liver + 9, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[10]},
|
|
{GORE_Liver + 10, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[11]},
|
|
{GORE_Liver + 11, GORE_Liver_RATE, DoShrapJumpFall, &s_GoreLiver[0]},
|
|
};
|
|
|
|
#define GORE_SkullCap 933
|
|
#define GORE_SkullCap_RATE 16
|
|
|
|
STATE s_GoreSkullCap[] =
|
|
{
|
|
{GORE_SkullCap + 0, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[1]},
|
|
{GORE_SkullCap + 1, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[2]},
|
|
{GORE_SkullCap + 2, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[3]},
|
|
{GORE_SkullCap + 3, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[4]},
|
|
{GORE_SkullCap + 4, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[5]},
|
|
{GORE_SkullCap + 5, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[6]},
|
|
{GORE_SkullCap + 6, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[7]},
|
|
{GORE_SkullCap + 7, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[8]},
|
|
{GORE_SkullCap + 8, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[9]},
|
|
{GORE_SkullCap + 9, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[10]},
|
|
{GORE_SkullCap + 10, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[11]},
|
|
{GORE_SkullCap + 11, GORE_SkullCap_RATE, DoShrapJumpFall, &s_GoreSkullCap[0]},
|
|
};
|
|
|
|
|
|
#define GORE_ChunkS 2430
|
|
#define GORE_ChunkS_RATE 16
|
|
|
|
STATE s_GoreChunkS[] =
|
|
{
|
|
{GORE_ChunkS + 0, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[1]},
|
|
{GORE_ChunkS + 1, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[2]},
|
|
{GORE_ChunkS + 2, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[3]},
|
|
{GORE_ChunkS + 3, GORE_ChunkS_RATE, DoShrapJumpFall, &s_GoreChunkS[0]},
|
|
};
|
|
|
|
#define GORE_Drip 1562 //2430
|
|
#define GORE_Drip_RATE 16
|
|
|
|
STATE s_GoreDrip[] =
|
|
{
|
|
{GORE_Drip + 0, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[1]},
|
|
{GORE_Drip + 1, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[2]},
|
|
{GORE_Drip + 2, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[3]},
|
|
{GORE_Drip + 3, GORE_Drip_RATE, DoShrapJumpFall, &s_GoreDrip[0]},
|
|
};
|
|
|
|
STATE s_FastGoreDrip[] =
|
|
{
|
|
{GORE_Drip + 0, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[1]},
|
|
{GORE_Drip + 1, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[2]},
|
|
{GORE_Drip + 2, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[3]},
|
|
{GORE_Drip + 3, GORE_Drip_RATE, DoFastShrapJumpFall, &s_FastGoreDrip[0]},
|
|
};
|
|
|
|
///////////////////////////////////////////////
|
|
//
|
|
// This GORE mostly for the Accursed Heads
|
|
//
|
|
///////////////////////////////////////////////
|
|
|
|
#define GORE_Flame 847
|
|
#define GORE_Flame_RATE 8
|
|
|
|
STATE s_GoreFlame[] =
|
|
{
|
|
{GORE_Flame + 0, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[1]},
|
|
{GORE_Flame + 1, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[2]},
|
|
{GORE_Flame + 2, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[3]},
|
|
{GORE_Flame + 3, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[4]},
|
|
{GORE_Flame + 4, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[5]},
|
|
{GORE_Flame + 5, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[6]},
|
|
{GORE_Flame + 6, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[7]},
|
|
{GORE_Flame + 7, GORE_Flame_RATE, DoFastShrapJumpFall, &s_GoreFlame[0]},
|
|
};
|
|
|
|
ANIMATOR DoTracerShrap;
|
|
STATE s_TracerShrap[] =
|
|
{
|
|
{GORE_Flame + 0, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[1]},
|
|
{GORE_Flame + 1, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[2]},
|
|
{GORE_Flame + 2, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[3]},
|
|
{GORE_Flame + 3, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[4]},
|
|
{GORE_Flame + 4, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[5]},
|
|
{GORE_Flame + 5, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[6]},
|
|
{GORE_Flame + 6, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[7]},
|
|
{GORE_Flame + 7, GORE_Flame_RATE, DoTracerShrap, &s_TracerShrap[0]},
|
|
};
|
|
|
|
#define UZI_SHELL 2152
|
|
#define UZISHELL_RATE 8
|
|
//ANIMATOR DoShellShrap;
|
|
STATE s_UziShellShrap[] =
|
|
{
|
|
{UZI_SHELL + 0, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[1]},
|
|
{UZI_SHELL + 1, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[2]},
|
|
{UZI_SHELL + 2, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[3]},
|
|
{UZI_SHELL + 3, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[4]},
|
|
{UZI_SHELL + 4, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[5]},
|
|
{UZI_SHELL + 5, UZISHELL_RATE, DoShrapJumpFall, &s_UziShellShrap[0]},
|
|
};
|
|
|
|
STATE s_UziShellShrapStill1[] =
|
|
{
|
|
{UZI_SHELL + 0, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill1[0]}
|
|
};
|
|
STATE s_UziShellShrapStill2[] =
|
|
{
|
|
{UZI_SHELL + 1, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill2[0]}
|
|
};
|
|
STATE s_UziShellShrapStill3[] =
|
|
{
|
|
{UZI_SHELL + 2, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill3[0]}
|
|
};
|
|
STATE s_UziShellShrapStill4[] =
|
|
{
|
|
{UZI_SHELL + 3, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill4[0]}
|
|
};
|
|
STATE s_UziShellShrapStill5[] =
|
|
{
|
|
{UZI_SHELL + 4, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill5[0]}
|
|
};
|
|
STATE s_UziShellShrapStill6[] =
|
|
{
|
|
{UZI_SHELL + 5, UZISHELL_RATE, NullAnimator, &s_UziShellShrapStill6[0]}
|
|
};
|
|
|
|
#define SHOT_SHELL 2180
|
|
#define SHOTSHELL_RATE 8
|
|
STATE s_ShotgunShellShrap[] =
|
|
{
|
|
{SHOT_SHELL + 0, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[1]},
|
|
{SHOT_SHELL + 1, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[2]},
|
|
{SHOT_SHELL + 2, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[3]},
|
|
{SHOT_SHELL + 3, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[4]},
|
|
{SHOT_SHELL + 4, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[5]},
|
|
{SHOT_SHELL + 5, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[6]},
|
|
{SHOT_SHELL + 6, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[7]},
|
|
{SHOT_SHELL + 7, SHOTSHELL_RATE, DoShrapJumpFall, &s_ShotgunShellShrap[0]},
|
|
};
|
|
|
|
STATE s_ShotgunShellShrapStill1[] =
|
|
{
|
|
{SHOT_SHELL + 1, SHOTSHELL_RATE, NullAnimator, &s_ShotgunShellShrapStill1[0]}
|
|
};
|
|
STATE s_ShotgunShellShrapStill2[] =
|
|
{
|
|
{SHOT_SHELL + 3, SHOTSHELL_RATE, NullAnimator, &s_ShotgunShellShrapStill2[0]}
|
|
};
|
|
STATE s_ShotgunShellShrapStill3[] =
|
|
{
|
|
{SHOT_SHELL + 7, SHOTSHELL_RATE, NullAnimator, &s_ShotgunShellShrapStill3[0]}
|
|
};
|
|
|
|
#define GORE_FlameChunkA 839
|
|
#define GORE_FlameChunkA_RATE 8
|
|
|
|
STATE s_GoreFlameChunkA[] =
|
|
{
|
|
{GORE_FlameChunkA + 0, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[1]},
|
|
{GORE_FlameChunkA + 1, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[2]},
|
|
{GORE_FlameChunkA + 2, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[3]},
|
|
{GORE_FlameChunkA + 3, GORE_FlameChunkA_RATE, DoShrapJumpFall, &s_GoreFlameChunkA[0]},
|
|
};
|
|
|
|
#define GORE_FlameChunkB 843
|
|
#define GORE_FlameChunkB_RATE 8
|
|
|
|
STATE s_GoreFlameChunkB[] =
|
|
{
|
|
{GORE_FlameChunkB + 0, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[1]},
|
|
{GORE_FlameChunkB + 1, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[2]},
|
|
{GORE_FlameChunkB + 2, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[3]},
|
|
{GORE_FlameChunkB + 3, GORE_FlameChunkB_RATE, DoShrapJumpFall, &s_GoreFlameChunkB[0]},
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// General Breaking Shrapnel
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
#define COIN_SHRAP 2530
|
|
#define CoinShrap_RATE 12
|
|
|
|
STATE s_CoinShrap[] =
|
|
{
|
|
{COIN_SHRAP + 0, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[1]},
|
|
{COIN_SHRAP + 1, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[2]},
|
|
{COIN_SHRAP + 2, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[3]},
|
|
{COIN_SHRAP + 3, CoinShrap_RATE, DoShrapJumpFall, &s_CoinShrap[0]},
|
|
};
|
|
|
|
#define MARBEL 5096
|
|
#define Marbel_RATE 12
|
|
|
|
STATE s_Marbel[] =
|
|
{
|
|
{MARBEL, Marbel_RATE, DoShrapJumpFall, &s_Marbel[0]},
|
|
};
|
|
|
|
//
|
|
// Glass
|
|
//
|
|
|
|
#define GLASS_SHRAP_A 3864
|
|
#define GlassShrapA_RATE 12
|
|
|
|
STATE s_GlassShrapA[] =
|
|
{
|
|
{GLASS_SHRAP_A + 0, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[1]},
|
|
{GLASS_SHRAP_A + 1, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[2]},
|
|
{GLASS_SHRAP_A + 2, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[3]},
|
|
{GLASS_SHRAP_A + 3, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[4]},
|
|
{GLASS_SHRAP_A + 4, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[5]},
|
|
{GLASS_SHRAP_A + 5, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[6]},
|
|
{GLASS_SHRAP_A + 6, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[7]},
|
|
{GLASS_SHRAP_A + 7, GlassShrapA_RATE, DoShrapJumpFall, &s_GlassShrapA[0]},
|
|
};
|
|
|
|
#define GLASS_SHRAP_B 3872
|
|
#define GlassShrapB_RATE 12
|
|
|
|
STATE s_GlassShrapB[] =
|
|
{
|
|
{GLASS_SHRAP_B + 0, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[1]},
|
|
{GLASS_SHRAP_B + 1, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[2]},
|
|
{GLASS_SHRAP_B + 2, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[3]},
|
|
{GLASS_SHRAP_B + 3, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[4]},
|
|
{GLASS_SHRAP_B + 4, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[5]},
|
|
{GLASS_SHRAP_B + 5, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[6]},
|
|
{GLASS_SHRAP_B + 6, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[7]},
|
|
{GLASS_SHRAP_B + 7, GlassShrapB_RATE, DoShrapJumpFall, &s_GlassShrapB[0]},
|
|
};
|
|
|
|
#define GLASS_SHRAP_C 3880
|
|
#define GlassShrapC_RATE 12
|
|
|
|
STATE s_GlassShrapC[] =
|
|
{
|
|
{GLASS_SHRAP_C + 0, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[1]},
|
|
{GLASS_SHRAP_C + 1, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[2]},
|
|
{GLASS_SHRAP_C + 2, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[3]},
|
|
{GLASS_SHRAP_C + 3, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[4]},
|
|
{GLASS_SHRAP_C + 4, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[5]},
|
|
{GLASS_SHRAP_C + 5, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[6]},
|
|
{GLASS_SHRAP_C + 6, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[7]},
|
|
{GLASS_SHRAP_C + 7, GlassShrapC_RATE, DoShrapJumpFall, &s_GlassShrapC[0]},
|
|
};
|
|
|
|
//
|
|
// Wood
|
|
//
|
|
|
|
#define WOOD_SHRAP_A 3924
|
|
#define WoodShrapA_RATE 12
|
|
|
|
STATE s_WoodShrapA[] =
|
|
{
|
|
{WOOD_SHRAP_A + 0, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[1]},
|
|
{WOOD_SHRAP_A + 1, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[2]},
|
|
{WOOD_SHRAP_A + 2, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[3]},
|
|
{WOOD_SHRAP_A + 3, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[4]},
|
|
{WOOD_SHRAP_A + 4, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[5]},
|
|
{WOOD_SHRAP_A + 5, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[6]},
|
|
{WOOD_SHRAP_A + 6, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[7]},
|
|
{WOOD_SHRAP_A + 7, WoodShrapA_RATE, DoShrapJumpFall, &s_WoodShrapA[0]},
|
|
};
|
|
|
|
#define WOOD_SHRAP_B 3932
|
|
#define WoodShrapB_RATE 12
|
|
|
|
STATE s_WoodShrapB[] =
|
|
{
|
|
{WOOD_SHRAP_B + 0, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[1]},
|
|
{WOOD_SHRAP_B + 1, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[2]},
|
|
{WOOD_SHRAP_B + 2, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[3]},
|
|
{WOOD_SHRAP_B + 3, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[4]},
|
|
{WOOD_SHRAP_B + 4, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[5]},
|
|
{WOOD_SHRAP_B + 5, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[6]},
|
|
{WOOD_SHRAP_B + 6, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[7]},
|
|
{WOOD_SHRAP_B + 7, WoodShrapB_RATE, DoShrapJumpFall, &s_WoodShrapB[0]},
|
|
};
|
|
|
|
#define WOOD_SHRAP_C 3941
|
|
#define WoodShrapC_RATE 12
|
|
|
|
STATE s_WoodShrapC[] =
|
|
{
|
|
{WOOD_SHRAP_C + 0, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[1]},
|
|
{WOOD_SHRAP_C + 1, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[2]},
|
|
{WOOD_SHRAP_C + 2, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[3]},
|
|
{WOOD_SHRAP_C + 3, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[4]},
|
|
{WOOD_SHRAP_C + 4, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[5]},
|
|
{WOOD_SHRAP_C + 5, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[6]},
|
|
{WOOD_SHRAP_C + 6, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[7]},
|
|
{WOOD_SHRAP_C + 7, WoodShrapC_RATE, DoShrapJumpFall, &s_WoodShrapC[0]},
|
|
};
|
|
|
|
//
|
|
// Stone
|
|
//
|
|
|
|
#define STONE_SHRAP_A 3840
|
|
#define StoneShrapA_RATE 12
|
|
|
|
STATE s_StoneShrapA[] =
|
|
{
|
|
{STONE_SHRAP_A + 0, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[1]},
|
|
{STONE_SHRAP_A + 1, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[2]},
|
|
{STONE_SHRAP_A + 2, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[3]},
|
|
{STONE_SHRAP_A + 3, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[4]},
|
|
{STONE_SHRAP_A + 4, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[5]},
|
|
{STONE_SHRAP_A + 5, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[6]},
|
|
{STONE_SHRAP_A + 6, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[7]},
|
|
{STONE_SHRAP_A + 7, StoneShrapA_RATE, DoShrapJumpFall, &s_StoneShrapA[0]},
|
|
};
|
|
|
|
#define STONE_SHRAP_B 3848
|
|
#define StoneShrapB_RATE 12
|
|
|
|
STATE s_StoneShrapB[] =
|
|
{
|
|
{STONE_SHRAP_B + 0, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[1]},
|
|
{STONE_SHRAP_B + 1, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[2]},
|
|
{STONE_SHRAP_B + 2, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[3]},
|
|
{STONE_SHRAP_B + 3, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[4]},
|
|
{STONE_SHRAP_B + 4, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[5]},
|
|
{STONE_SHRAP_B + 5, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[6]},
|
|
{STONE_SHRAP_B + 6, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[7]},
|
|
{STONE_SHRAP_B + 7, StoneShrapB_RATE, DoShrapJumpFall, &s_StoneShrapB[0]},
|
|
};
|
|
|
|
#define STONE_SHRAP_C 3856
|
|
#define StoneShrapC_RATE 12
|
|
|
|
STATE s_StoneShrapC[] =
|
|
{
|
|
{STONE_SHRAP_C + 0, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[1]},
|
|
{STONE_SHRAP_C + 1, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[2]},
|
|
{STONE_SHRAP_C + 2, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[3]},
|
|
{STONE_SHRAP_C + 3, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[4]},
|
|
{STONE_SHRAP_C + 4, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[5]},
|
|
{STONE_SHRAP_C + 5, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[6]},
|
|
{STONE_SHRAP_C + 6, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[7]},
|
|
{STONE_SHRAP_C + 7, StoneShrapC_RATE, DoShrapJumpFall, &s_StoneShrapC[0]},
|
|
};
|
|
|
|
//
|
|
// Metal
|
|
//
|
|
|
|
#define METAL_SHRAP_A 3888
|
|
#define MetalShrapA_RATE 12
|
|
|
|
STATE s_MetalShrapA[] =
|
|
{
|
|
{METAL_SHRAP_A + 0, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[1]},
|
|
{METAL_SHRAP_A + 1, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[2]},
|
|
{METAL_SHRAP_A + 2, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[3]},
|
|
{METAL_SHRAP_A + 3, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[4]},
|
|
{METAL_SHRAP_A + 4, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[5]},
|
|
{METAL_SHRAP_A + 5, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[6]},
|
|
{METAL_SHRAP_A + 6, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[7]},
|
|
{METAL_SHRAP_A + 7, MetalShrapA_RATE, DoShrapJumpFall, &s_MetalShrapA[0]},
|
|
};
|
|
|
|
#define METAL_SHRAP_B 3896
|
|
#define MetalShrapB_RATE 12
|
|
|
|
STATE s_MetalShrapB[] =
|
|
{
|
|
{METAL_SHRAP_B + 0, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[1]},
|
|
{METAL_SHRAP_B + 1, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[2]},
|
|
{METAL_SHRAP_B + 2, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[3]},
|
|
{METAL_SHRAP_B + 3, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[4]},
|
|
{METAL_SHRAP_B + 4, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[5]},
|
|
{METAL_SHRAP_B + 5, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[6]},
|
|
{METAL_SHRAP_B + 6, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[7]},
|
|
{METAL_SHRAP_B + 7, MetalShrapB_RATE, DoShrapJumpFall, &s_MetalShrapB[0]},
|
|
};
|
|
|
|
#define METAL_SHRAP_C 3904
|
|
#define MetalShrapC_RATE 12
|
|
|
|
STATE s_MetalShrapC[] =
|
|
{
|
|
{METAL_SHRAP_C + 0, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[1]},
|
|
{METAL_SHRAP_C + 1, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[2]},
|
|
{METAL_SHRAP_C + 2, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[3]},
|
|
{METAL_SHRAP_C + 3, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[4]},
|
|
{METAL_SHRAP_C + 4, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[5]},
|
|
{METAL_SHRAP_C + 5, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[6]},
|
|
{METAL_SHRAP_C + 6, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[7]},
|
|
{METAL_SHRAP_C + 7, MetalShrapC_RATE, DoShrapJumpFall, &s_MetalShrapC[0]},
|
|
};
|
|
|
|
//
|
|
// Paper
|
|
//
|
|
|
|
#define PAPER_SHRAP_A 3924
|
|
#define PaperShrapA_RATE 12
|
|
|
|
STATE s_PaperShrapA[] =
|
|
{
|
|
{PAPER_SHRAP_A + 0, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[1]},
|
|
{PAPER_SHRAP_A + 1, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[2]},
|
|
{PAPER_SHRAP_A + 2, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[3]},
|
|
{PAPER_SHRAP_A + 3, PaperShrapA_RATE, DoShrapJumpFall, &s_PaperShrapA[0]},
|
|
};
|
|
|
|
#define PAPER_SHRAP_B 3932
|
|
#define PaperShrapB_RATE 12
|
|
|
|
STATE s_PaperShrapB[] =
|
|
{
|
|
{PAPER_SHRAP_B + 0, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[1]},
|
|
{PAPER_SHRAP_B + 1, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[2]},
|
|
{PAPER_SHRAP_B + 2, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[3]},
|
|
{PAPER_SHRAP_B + 3, PaperShrapB_RATE, DoShrapJumpFall, &s_PaperShrapB[0]},
|
|
};
|
|
|
|
#define PAPER_SHRAP_C 3941
|
|
#define PaperShrapC_RATE 12
|
|
|
|
STATE s_PaperShrapC[] =
|
|
{
|
|
{PAPER_SHRAP_C + 0, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[1]},
|
|
{PAPER_SHRAP_C + 1, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[2]},
|
|
{PAPER_SHRAP_C + 2, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[3]},
|
|
{PAPER_SHRAP_C + 3, PaperShrapC_RATE, DoShrapJumpFall, &s_PaperShrapC[0]},
|
|
};
|
|
|
|
#if 1
|
|
SWBOOL MissileHitMatch(short Weapon, short WeaponNum, short hit_sprite)
|
|
{
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
|
|
if (WeaponNum <= -1)
|
|
{
|
|
ASSERT(Weapon >= 0);
|
|
USERp wu = User[Weapon];
|
|
WeaponNum = wu->WeaponNum;
|
|
|
|
// can be hit by SO only
|
|
if (SP_TAG7(hsp) == 4)
|
|
{
|
|
if (TEST(wu->Flags2, SPR2_SO_MISSILE))
|
|
{
|
|
DoMatchEverything(NULL, hsp->hitag, -1);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SP_TAG7(hsp) == 0)
|
|
{
|
|
switch (WeaponNum)
|
|
{
|
|
case WPN_RAIL:
|
|
case WPN_MICRO:
|
|
case WPN_NAPALM:
|
|
case WPN_ROCKET:
|
|
DoMatchEverything(NULL, hsp->hitag, -1);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (SP_TAG7(hsp) == 1)
|
|
{
|
|
switch (WeaponNum)
|
|
{
|
|
case WPN_MICRO:
|
|
case WPN_RAIL:
|
|
case WPN_HOTHEAD:
|
|
case WPN_NAPALM:
|
|
case WPN_ROCKET:
|
|
DoMatchEverything(NULL, hsp->hitag, -1);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (SP_TAG7(hsp) == 2)
|
|
{
|
|
switch (WeaponNum)
|
|
{
|
|
case WPN_MICRO:
|
|
case WPN_RAIL:
|
|
case WPN_HOTHEAD:
|
|
case WPN_NAPALM:
|
|
case WPN_ROCKET:
|
|
case WPN_UZI:
|
|
case WPN_SHOTGUN:
|
|
DoMatchEverything(NULL, hsp->hitag, -1);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else if (SP_TAG7(hsp) == 3)
|
|
{
|
|
switch (WeaponNum)
|
|
{
|
|
case WPN_STAR:
|
|
case WPN_SWORD:
|
|
case WPN_FIST:
|
|
case WPN_MICRO:
|
|
case WPN_RAIL:
|
|
case WPN_HOTHEAD:
|
|
case WPN_NAPALM:
|
|
case WPN_ROCKET:
|
|
case WPN_UZI:
|
|
case WPN_SHOTGUN:
|
|
DoMatchEverything(NULL, hsp->hitag, -1);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
#if 0
|
|
WPN_STAR
|
|
WPN_UZI
|
|
WPN_SHOTGUN
|
|
WPN_MICRO
|
|
WPN_GRENADE
|
|
WPN_MINE
|
|
WPN_RAIL
|
|
WPN_HEART
|
|
WPN_HOTHEAD
|
|
WPN_NAPALM
|
|
WPN_RING
|
|
WPN_ROCKET
|
|
WPN_SWORD
|
|
WPN_FIST
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int SpawnShrapX(short SpriteNum)
|
|
{
|
|
//For shrap that has no Weapon to send over
|
|
SpawnShrap(SpriteNum, -1);
|
|
return 0;
|
|
}
|
|
|
|
int DoLavaErupt(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
short i,nexti,pnum;
|
|
PLAYERp pp;
|
|
SPRITEp tsp;
|
|
SWBOOL found = FALSE;
|
|
|
|
if (TEST_BOOL1(sp))
|
|
{
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
pp = Player + pnum;
|
|
if (pp->cursectnum >= 0 && TEST(sector[pp->cursectnum].extra, SECTFX_TRIGGER))
|
|
{
|
|
TRAVERSE_SPRITE_SECT(headspritesect[pp->cursectnum],i,nexti)
|
|
{
|
|
tsp = &sprite[i];
|
|
|
|
if (tsp->statnum == STAT_TRIGGER && SP_TAG7(tsp) == 0 && SP_TAG5(tsp) == 1)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return 0;
|
|
}
|
|
|
|
if (!TEST(u->Flags, SPR_ACTIVE))
|
|
{
|
|
// inactive
|
|
if ((u->WaitTics -= synctics) <= 0)
|
|
{
|
|
SET(u->Flags, SPR_ACTIVE);
|
|
u->Counter = 0;
|
|
u->WaitTics = SP_TAG9(sp) * 120L;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// active
|
|
if ((u->WaitTics -= synctics) <= 0)
|
|
{
|
|
// Stop for this long
|
|
RESET(u->Flags, SPR_ACTIVE);
|
|
u->Counter = 0;
|
|
u->WaitTics = SP_TAG10(sp) * 120L;
|
|
}
|
|
|
|
// Counter controls the volume of lava erupting
|
|
// starts out slow and increases to a max
|
|
u->Counter += synctics;
|
|
if (u->Counter > SP_TAG2(sp))
|
|
u->Counter = SP_TAG2(sp);
|
|
|
|
if ((RANDOM_P2(1024<<6)>>6) < u->Counter)
|
|
{
|
|
switch (SP_TAG3(sp))
|
|
{
|
|
case 0:
|
|
SpawnShrapX(SpriteNum);
|
|
break;
|
|
case 1:
|
|
InitVulcanBoulder(SpriteNum);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void UserShrapSetup(SHRAPp shrap, STATEp state, int num_shrap)
|
|
{
|
|
shrap->state = state;
|
|
shrap->num = num_shrap;
|
|
}
|
|
|
|
#if 0
|
|
STATEp UserStateSetup(short base_tile, short num_tiles)
|
|
{
|
|
STATEp base_state, stp;
|
|
short tile;
|
|
|
|
#define USER_PIC 0
|
|
#define USER_RATE 12
|
|
|
|
base_state = CallocMem(sizeof(STATE) * num_tiles, 1);
|
|
|
|
for (stp = base_state, tile = 0; stp->state || tile < num_tiles; stp++, tile++)
|
|
{
|
|
stp->Pic = base_tile + tile;
|
|
stp->Tics = USER_RATE;
|
|
stp->Animator = DoShrapJumpFall;
|
|
|
|
// if last tile
|
|
if (tile == num_tiles - 1)
|
|
stp->NextState = base_state;
|
|
else
|
|
stp->NextState = stp + 1;
|
|
}
|
|
|
|
return base_state;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
SpawnShrap(short ParentNum, short Secondary)
|
|
{
|
|
SPRITEp parent = &sprite[ParentNum];
|
|
SPRITEp sp;
|
|
USERp u, pu = User[ParentNum];
|
|
short SpriteNum;
|
|
short i;
|
|
|
|
/////////////////////////////////////////
|
|
//
|
|
// BREAK shrap types
|
|
//
|
|
/////////////////////////////////////////
|
|
|
|
// Individual shraps can be copied to this and then values can be changed
|
|
static SHRAP CustomShrap[20];
|
|
|
|
static SHRAP CoinShrap[] =
|
|
{
|
|
{s_CoinShrap, COIN_SHRAP, 5, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP GlassShrap[] =
|
|
{
|
|
{s_GlassShrapA, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapB, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapC, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP WoodShrap[] =
|
|
{
|
|
{s_WoodShrapA, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_WoodShrapB, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_WoodShrapC, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP StoneShrap[] =
|
|
{
|
|
{s_StoneShrapA, STONE_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_StoneShrapB, STONE_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_StoneShrapC, STONE_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP PaperShrap[] =
|
|
{
|
|
{s_PaperShrapA, PAPER_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_PaperShrapB, PAPER_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_PaperShrapC, PAPER_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP MetalShrap[] =
|
|
{
|
|
{s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP MetalMix[] =
|
|
{
|
|
{s_GlassShrapA, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapB, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapC, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP WoodMix[] =
|
|
{
|
|
{s_WoodShrapA, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_WoodShrapB, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_WoodShrapC, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapA, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapB, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_MetalShrapC, METAL_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP PaperMix[] =
|
|
{
|
|
{s_WoodShrapA, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_WoodShrapB, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_WoodShrapC, WOOD_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_PaperShrapA, PAPER_SHRAP_A, 2, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_PaperShrapB, PAPER_SHRAP_A, 2, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_PaperShrapC, PAPER_SHRAP_A, 2, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP Marbels[] =
|
|
{
|
|
{s_Marbel, MARBEL, 5, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapA, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapB, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{s_GlassShrapC, GLASS_SHRAP_A, 1, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#if 0
|
|
static SHRAP BloodShrap[] =
|
|
{
|
|
{s_BloodShrap, BLOOD_SHRAP, 8, Z_MID, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL},
|
|
};
|
|
#endif
|
|
|
|
////
|
|
// END - BREAK shrap types
|
|
////
|
|
|
|
static SHRAP EMPShrap[] =
|
|
{
|
|
{s_EMPShrap, EMP, 1, Z_MID, 500, 1100, 300, 600, FALSE, 128},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP StdShrap[] =
|
|
{
|
|
{s_GoreHead, GORE_Head, 1, Z_TOP, 400, 700, 20, 40, TRUE, 2048},
|
|
{s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 800, 100, 300, TRUE, 2048},
|
|
{s_GoreLiver, GORE_Liver, 1, Z_MID, 300, 500, 100, 150, TRUE, 2048},
|
|
{s_GoreArm, GORE_Arm, 1, Z_MID, 300, 500, 250, 500, TRUE, 2048},
|
|
{s_GoreSkullCap, GORE_SkullCap, 1, Z_TOP, 300, 500, 250, 500, TRUE, 2048},
|
|
{s_FastGoreDrip, GORE_Drip, 8, Z_BOT, 600, 800, 50, 70, FALSE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP HeartAttackShrap[] = // fewer gibs because of the plasma fountain sprites
|
|
{
|
|
{s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 1100, 300, 600, TRUE, 2048},
|
|
{s_GoreLiver, GORE_Liver, 1, Z_MID, 500, 1100, 300, 500, TRUE, 2048},
|
|
{s_GoreArm, GORE_Arm, 2, Z_MID, 500, 1100, 350, 600, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP SkelGore[] =
|
|
{
|
|
{s_GoreHead, GORE_Head, 1, Z_TOP, 400, 700, 20, 40, TRUE, 2048},
|
|
{s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 800, 100, 300, TRUE, 2048},
|
|
{s_GoreLiver, GORE_Liver, 1, Z_MID, 300, 500, 100, 150, TRUE, 2048},
|
|
{s_GoreSkullCap, GORE_SkullCap, 1, Z_TOP, 300, 500, 100, 150, TRUE, 2048},
|
|
{s_GoreArm, GORE_Arm, 1, Z_MID, 300, 500, 250, 500, TRUE, 2048},
|
|
{s_GoreLeg, GORE_Leg, 2, Z_BOT, 200, 400, 250, 500, TRUE, 2048},
|
|
{s_GoreChunkS, GORE_ChunkS, 4, Z_BOT, 200, 400, 250, 400, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP UpperGore[] =
|
|
{
|
|
{s_GoreHead, GORE_Head, 1, Z_TOP, 400, 700, 20, 40, TRUE, 2048},
|
|
{s_GoreLung, GORE_Lung, 2, Z_TOP, 500, 800, 100, 300, TRUE, 2048},
|
|
{s_GoreLiver, GORE_Liver, 1, Z_MID, 300, 500, 100, 150, TRUE, 2048},
|
|
{s_GoreSkullCap, GORE_SkullCap, 1, Z_TOP, 300, 500, 100, 150, TRUE, 2048},
|
|
{s_GoreArm, GORE_Arm, 1, Z_MID, 300, 500, 250, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#if 0
|
|
static SHRAP LowerGore[] =
|
|
{
|
|
{s_GoreLeg, GORE_Leg, 4, Z_BOT, 300, 500, 100, 200, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
#endif
|
|
|
|
static SHRAP SmallGore[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 3, Z_TOP, 600, 800, 50, 70, FALSE, 2048},
|
|
{s_FastGoreDrip, GORE_Drip, 3, Z_BOT, 600, 800, 70, 100, FALSE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP FlamingGore[] =
|
|
{
|
|
{s_GoreFlame, GORE_Drip, 2, Z_TOP, 600, 800, 100, 200, FALSE, 2048},
|
|
{s_GoreFlameChunkB, GORE_Drip, 4, Z_MID, 300, 500, 100, 200, FALSE, 2048},
|
|
{s_GoreFlame, GORE_Drip, 2, Z_BOT, 100, 200, 100, 200, FALSE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#if 0
|
|
static SHRAP BoltExpShrap[] =
|
|
{
|
|
{s_GoreFlame, GORE_Drip, 4, Z_MID, 300, 700, 300, 600, TRUE, 2048},
|
|
{s_GoreFlame, GORE_Drip, 4, Z_BOT, 300, 700, 300, 600, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP TracerExpShrap[] =
|
|
{
|
|
{s_TracerShrap, GORE_Drip, 3, Z_MID, 300, 700, 300, 600, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP FireballExpShrap1[] =
|
|
{
|
|
{s_GoreFlame, GORE_Drip, 1, Z_MID, 100, 300, 100, 200, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP FireballExpShrap2[] =
|
|
{
|
|
{s_GoreFlame, GORE_Drip, 2, Z_MID, 100, 300, 100, 200, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAPp FireballExpShrap[] =
|
|
{
|
|
FireballExpShrap1,
|
|
FireballExpShrap2
|
|
};
|
|
#endif
|
|
|
|
// state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel,
|
|
// random_disperse, ang_range;
|
|
static SHRAP ElectroShrap[] =
|
|
{
|
|
{s_ElectroShrap, ELECTRO_SHARD, 12, Z_TOP, 200, 600, 100, 500, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
// state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel,
|
|
// random_disperse, ang_range;
|
|
static SHRAP LavaShrap1[] =
|
|
{
|
|
{s_GoreFlame, GORE_Drip, 1, Z_TOP, 400, 1400, 100, 400, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP LavaShrap2[] =
|
|
{
|
|
{s_GoreFlameChunkB, GORE_Drip, 1, Z_TOP, 400, 1400, 100, 400, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP *LavaShrapTable[] =
|
|
{
|
|
LavaShrap1,
|
|
LavaShrap2
|
|
};
|
|
|
|
static SHRAP LavaBoulderShrap[] =
|
|
{
|
|
{s_LavaShard, LAVA_SHARD, 16, Z_MID, 400, 900, 200, 600, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#if 0
|
|
static SHRAP SectorSquishGore[] =
|
|
{
|
|
{s_FastGoreDrip, GORE_Drip, 24, Z_MID, -400, -200, 600, 800, FALSE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
// PLAYER SHRAP
|
|
//
|
|
|
|
// state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel,
|
|
// random_disperse, ang_range;
|
|
static SHRAP PlayerGoreFall[] =
|
|
{
|
|
{s_GoreSkullCap,GORE_SkullCap, 1, Z_TOP, 200, 300, 100, 200, TRUE, 2048},
|
|
{s_GoreLiver, GORE_Liver, 1, Z_MID, 200, 300, 100, 200, TRUE, 2048},
|
|
{s_GoreLung, GORE_Lung, 1, Z_MID, 200, 300, 100, 200, TRUE, 2048},
|
|
{s_GoreDrip, GORE_Drip, 10, Z_MID, 200, 300, 100, 200, FALSE, 2048},
|
|
{s_GoreArm, GORE_Arm, 1, Z_MID, 200, 300, 100, 200, TRUE, 2048},
|
|
{s_FastGoreDrip, GORE_Drip, 10, Z_BOT, 200, 300, 100, 200, FALSE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP PlayerGoreFly[] =
|
|
{
|
|
{s_GoreSkullCap,GORE_SkullCap, 1, Z_TOP, 500, 1100, 300, 600, TRUE, 2048},
|
|
{s_GoreTorso, GORE_Torso, 1, Z_MID, 500, 1100, 300, 500, TRUE, 2048},
|
|
{s_GoreLiver, GORE_Liver, 1, Z_MID, 200, 300, 100, 200, TRUE, 2048},
|
|
{s_GoreArm, GORE_Arm, 1, Z_MID, 500, 1100, 350, 600, TRUE, 2048},
|
|
{s_FastGoreDrip, GORE_Drip, 16, Z_MID, 500, 1100, 350, 600, FALSE, 2048},
|
|
{s_FastGoreDrip, GORE_Drip, 16, Z_BOT, 500, 1100, 350, 600, FALSE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP PlayerDeadHead[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 2, Z_TOP, 150, 400, 40, 80, TRUE, 2048},
|
|
{s_GoreDrip, GORE_Drip, 2, Z_MID, 150, 400, 40, 80, TRUE, 2048},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
// state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel,
|
|
// random_disperse, ang_range;
|
|
static SHRAP PlayerHeadHurl1[] =
|
|
{
|
|
{s_Vomit1, Vomit1, 1, Z_BOT, 250, 400, 100, 200, TRUE, 256},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#define WALL_FLOOR_SHRAP 4097
|
|
ANIMATOR DoShrapWallFloor;
|
|
#if 0
|
|
static SHRAP SectorExpShrap[] =
|
|
{
|
|
{NULL, WALL_FLOOR_SHRAP, 1, Z_BOT, 550, 800, 200, 400, TRUE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
#endif
|
|
|
|
int hz[3];
|
|
short dang = 0;
|
|
|
|
//SHRAPp p = StdShrap;
|
|
SHRAPp p = SmallGore;
|
|
short shrap_shade = -15;
|
|
short shrap_xsize = 48, shrap_ysize = 48;
|
|
short retval = TRUE;
|
|
short shrap_pal = PALETTE_DEFAULT;
|
|
int shrap_floor_dist = Z(2);
|
|
int shrap_ceiling_dist = Z(2);
|
|
int nx,ny;
|
|
short jump_grav = ACTOR_GRAVITY;
|
|
short start_ang = 0;
|
|
short shrap_owner = -1;
|
|
int shrap_bounce = FALSE;
|
|
short WaitTics = 64; // for FastShrap
|
|
short shrap_type;
|
|
int shrap_rand_zamt = 0;
|
|
short shrap_ang = parent->ang;
|
|
short shrap_delta_size = 0;
|
|
short shrap_amt = 0;
|
|
extern BREAK_INFOp GlobBreakInfo;
|
|
|
|
if (Prediction)
|
|
return 0;
|
|
|
|
// Don't spawn shrapnel in invalid sectors gosh dern it!
|
|
if (parent->sectnum < 0 || parent->sectnum >= MAXSECTORS)
|
|
{
|
|
//DSPRINTF(ds,"SpawnShrap: Invalid sector %d, picnum=%d\n",parent->sectnum,parent->picnum);
|
|
MONO_PRINT(ds);
|
|
return 0;
|
|
}
|
|
|
|
if (GlobBreakInfo)
|
|
{
|
|
shrap_type = GlobBreakInfo->shrap_type;
|
|
shrap_amt = GlobBreakInfo->shrap_amt;
|
|
GlobBreakInfo = NULL;
|
|
goto AutoShrap;
|
|
}
|
|
else if (TEST(parent->extra, SPRX_BREAKABLE))
|
|
{
|
|
// if no user
|
|
if (!User[parent - sprite])
|
|
{
|
|
// Jump to shrap type
|
|
shrap_type = SP_TAG8(parent);
|
|
goto UserShrap;
|
|
}
|
|
else
|
|
{
|
|
// has a user - is programmed
|
|
change_sprite_stat(parent - sprite, STAT_MISC);
|
|
RESET(parent->extra, SPRX_BREAKABLE);
|
|
RESET(parent->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
}
|
|
}
|
|
|
|
ASSERT(pu);
|
|
|
|
switch (pu->ID)
|
|
{
|
|
case ST1:
|
|
switch (parent->hitag)
|
|
{
|
|
case SPAWN_SPOT:
|
|
{
|
|
if (pu->LastDamage)
|
|
shrap_type = SP_TAG3(parent);
|
|
else
|
|
shrap_type = SP_TAG6(parent);
|
|
|
|
UserShrap:
|
|
|
|
shrap_delta_size = (signed char)SP_TAG10(parent);
|
|
shrap_rand_zamt = SP_TAG9(parent);
|
|
// Hey, better limit this in case mappers go crazy, like I did. :)
|
|
// Kills frame rate!
|
|
shrap_amt = SP_TAG8(parent);
|
|
if (shrap_amt > 5)
|
|
shrap_amt = 5;
|
|
|
|
AutoShrap:
|
|
|
|
switch (shrap_type)
|
|
{
|
|
case SHRAP_NONE:
|
|
return FALSE;
|
|
|
|
case SHRAP_GLASS:
|
|
PlaySound(DIGI_BREAKGLASS,parent,v3df_dontpan|v3df_doppler);
|
|
p = GlassShrap;
|
|
//BreakShrapSetup(GlassShrap, sizeof(GlassShrap), CustomShrap, shrap_amt);
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, GlassShrap, sizeof(GlassShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_GENERIC:
|
|
case SHRAP_STONE:
|
|
PlaySound(DIGI_BREAKSTONES,parent,v3df_dontpan|v3df_doppler);
|
|
p = StoneShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, StoneShrap, sizeof(StoneShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 8 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_WOOD:
|
|
PlaySound(DIGI_BREAKINGWOOD,parent,v3df_dontpan|v3df_doppler);
|
|
p = WoodShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, WoodShrap, sizeof(WoodShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_BLOOD:
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
break;
|
|
|
|
case SHRAP_GIBS:
|
|
PlaySound(DIGI_GIBS1,parent,v3df_dontpan|v3df_doppler);
|
|
p = SmallGore;
|
|
shrap_xsize = shrap_ysize = 34;
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
|
|
case SHRAP_TREE_BARK:
|
|
PlaySound(DIGI_BREAKINGWOOD,parent,v3df_dontpan|v3df_doppler);
|
|
p = WoodShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, WoodShrap, sizeof(WoodShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_PAPER:
|
|
p = PaperShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, PaperShrap, sizeof(PaperShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
break;
|
|
|
|
case SHRAP_METAL:
|
|
PlaySound(DIGI_BREAKMETAL,parent,v3df_dontpan|v3df_doppler);
|
|
p = MetalShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, MetalShrap, sizeof(MetalShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
|
|
case SHRAP_COIN:
|
|
PlaySound(DIGI_COINS,parent,v3df_dontpan|v3df_doppler);
|
|
p = CoinShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, CoinShrap, sizeof(CoinShrap));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_METALMIX:
|
|
PlaySound(DIGI_BREAKMETAL,parent,v3df_dontpan|v3df_doppler);
|
|
p = MetalMix;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, MetalMix, sizeof(MetalMix));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_MARBELS:
|
|
{
|
|
#if 0
|
|
short spnum;
|
|
short size;
|
|
SPRITEp ep;
|
|
USERp eu;
|
|
int SpawnLittleExp(int16_t Weapon);
|
|
|
|
spnum = SpawnLittleExp(ParentNum);
|
|
ASSERT(spnum >= 0);
|
|
ep = &sprite[spnum];
|
|
eu = User[spnum];
|
|
|
|
//eu->xchange = MOVEx(92, ep->ang);
|
|
//eu->ychange = MOVEy(92, ep->ang);
|
|
|
|
size = ep->xrepeat/2;
|
|
ep->xrepeat = ep->yrepeat = size + shrap_delta_size;
|
|
#endif
|
|
|
|
//PlaySound(DIGI_BREAKMARBELS,parent,v3df_dontpan|v3df_doppler);
|
|
p = Marbels;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, Marbels, sizeof(Marbels));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 10 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
}
|
|
break;
|
|
|
|
case SHRAP_WOODMIX:
|
|
PlaySound(DIGI_BREAKINGWOOD,parent,v3df_dontpan|v3df_doppler);
|
|
p = WoodMix;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, WoodMix, sizeof(WoodMix));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
|
|
case SHRAP_PAPERMIX:
|
|
PlaySound(DIGI_BREAKINGWOOD,parent,v3df_dontpan|v3df_doppler);
|
|
p = PaperMix;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, PaperMix, sizeof(PaperMix));
|
|
CustomShrap->num = shrap_amt;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 16 + shrap_delta_size;
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
|
|
case SHRAP_SO_SMOKE:
|
|
return FALSE;
|
|
|
|
case SHRAP_EXPLOSION:
|
|
{
|
|
short spnum;
|
|
short size;
|
|
SPRITEp ep;
|
|
// USERp eu;
|
|
|
|
spnum = SpawnLargeExp(ParentNum);
|
|
ASSERT(spnum >= 0);
|
|
//spnum = SpawnSectorExp(ParentNum);
|
|
ep = &sprite[spnum];
|
|
// eu = User[spnum];
|
|
|
|
//eu->xchange = MOVEx(92, ep->ang);
|
|
//eu->ychange = MOVEy(92, ep->ang);
|
|
|
|
size = ep->xrepeat;
|
|
ep->xrepeat = ep->yrepeat = size + shrap_delta_size;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
case SHRAP_LARGE_EXPLOSION:
|
|
{
|
|
short spnum;
|
|
short size;
|
|
SPRITEp ep;
|
|
// USERp eu;
|
|
|
|
//spnum = SpawnSectorExp(ParentNum);
|
|
spnum = SpawnLargeExp(ParentNum);
|
|
ASSERT(spnum >= 0);
|
|
ep = &sprite[spnum];
|
|
// eu = User[spnum];
|
|
|
|
//eu->xchange = MOVEx(92, ep->ang);
|
|
//eu->ychange = MOVEy(92, ep->ang);
|
|
|
|
size = ep->xrepeat;
|
|
ep->xrepeat = ep->yrepeat = size + shrap_delta_size;
|
|
|
|
InitPhosphorus(spnum);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
p = LavaShrapTable[RANDOM_P2(2<<8)>>8];
|
|
}
|
|
break;
|
|
|
|
case BREAK_BARREL:
|
|
PlaySound(DIGI_BREAKDEBRIS,parent,v3df_dontpan|v3df_doppler);
|
|
p = WoodShrap;
|
|
shrap_xsize = shrap_ysize = 24;
|
|
shrap_bounce = TRUE;
|
|
ChangeState(parent - sprite, s_BreakBarrel);
|
|
break;
|
|
case BREAK_LIGHT:
|
|
PlaySound(DIGI_BREAKGLASS,parent,v3df_dontpan|v3df_doppler);
|
|
p = GlassShrap;
|
|
shrap_xsize = shrap_ysize = 24;
|
|
shrap_bounce = TRUE;
|
|
ChangeState(parent - sprite, s_BreakLight);
|
|
break;
|
|
case BREAK_PEDISTAL:
|
|
PlaySound(DIGI_BREAKSTONES,parent,v3df_dontpan|v3df_doppler);
|
|
p = StoneShrap;
|
|
shrap_xsize = shrap_ysize = 24;
|
|
shrap_bounce = TRUE;
|
|
ChangeState(parent - sprite, s_BreakPedistal);
|
|
break;
|
|
case BREAK_BOTTLE1:
|
|
PlaySound(DIGI_BREAKGLASS,parent,v3df_dontpan|v3df_doppler);
|
|
p = GlassShrap;
|
|
shrap_xsize = shrap_ysize = 8;
|
|
shrap_bounce = TRUE;
|
|
ChangeState(parent - sprite, s_BreakBottle1);
|
|
break;
|
|
case BREAK_BOTTLE2:
|
|
PlaySound(DIGI_BREAKGLASS,parent,v3df_dontpan|v3df_doppler);
|
|
p = GlassShrap;
|
|
shrap_xsize = shrap_ysize = 8;
|
|
shrap_bounce = TRUE;
|
|
ChangeState(parent - sprite, s_BreakBottle2);
|
|
break;
|
|
case BREAK_MUSHROOM:
|
|
PlaySound(DIGI_BREAKDEBRIS,parent,v3df_dontpan|v3df_doppler);
|
|
p = StoneShrap;
|
|
shrap_xsize = shrap_ysize = 4;
|
|
shrap_bounce = TRUE;
|
|
SetSuicide(parent - sprite); // kill next iteration
|
|
break;
|
|
case BOLT_EXP:
|
|
return FALSE;
|
|
// p = BoltExpShrap;
|
|
// break;
|
|
case TANK_SHELL_EXP:
|
|
return FALSE;
|
|
// p = BoltExpShrap;
|
|
// break;
|
|
case TRACER_EXP:
|
|
return FALSE;
|
|
// p = TracerExpShrap;
|
|
// shrap_xsize = shrap_ysize = 20;
|
|
// WaitTics = 10;
|
|
// break;
|
|
case BOLT_THINMAN_R1:
|
|
p = MetalShrap;
|
|
if (shrap_amt)
|
|
{
|
|
memcpy(CustomShrap, MetalShrap, sizeof(MetalShrap));
|
|
CustomShrap->num = 1;
|
|
p = CustomShrap;
|
|
}
|
|
|
|
shrap_xsize = shrap_ysize = 10;
|
|
break;
|
|
case LAVA_BOULDER:
|
|
PlaySound(DIGI_BREAKSTONES,parent,v3df_dontpan|v3df_doppler);
|
|
p = LavaBoulderShrap;
|
|
shrap_owner = parent->owner;
|
|
shrap_xsize = shrap_ysize = 24;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
case SECTOR_EXP:
|
|
//p = SectorExpShrap;
|
|
//break;
|
|
return FALSE;
|
|
case GRENADE_EXP:
|
|
//p = SectorExpShrap;
|
|
//break;
|
|
return FALSE;
|
|
case FIREBALL_EXP:
|
|
return FALSE;
|
|
// p = FireballExpShrap[RANDOM_P2(2<<8)>>8];
|
|
// shrap_pal = pu->spal;
|
|
break;
|
|
case ELECTRO_PLAYER:
|
|
case ELECTRO_ENEMY:
|
|
shrap_owner = parent->owner;
|
|
p = ElectroShrap;
|
|
shrap_xsize = shrap_ysize = 20;
|
|
break;
|
|
case COOLIE_RUN_R0:
|
|
if (Secondary == WPN_NM_SECTOR_SQUISH)
|
|
break;
|
|
// return (FALSE);
|
|
break;
|
|
case NINJA_DEAD:
|
|
return FALSE;
|
|
break;
|
|
case NINJA_Head_R0:
|
|
{
|
|
extern STATEp sg_PlayerHeadHurl[];
|
|
|
|
if (pu->Rot == sg_PlayerHeadHurl)
|
|
{
|
|
p = PlayerHeadHurl1;
|
|
}
|
|
else
|
|
{
|
|
p = PlayerDeadHead;
|
|
shrap_xsize = shrap_ysize = 16+8;
|
|
shrap_bounce = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
case GIRLNINJA_RUN_R0:
|
|
p = StdShrap;
|
|
break;
|
|
case NINJA_RUN_R0:
|
|
{
|
|
p = StdShrap;
|
|
if (pu->PlayerP)
|
|
{
|
|
PLAYERp pp = pu->PlayerP;
|
|
|
|
if (pp->DeathType == PLAYER_DEATH_CRUMBLE)
|
|
p = PlayerGoreFall;
|
|
else
|
|
p = PlayerGoreFly;
|
|
}
|
|
break;
|
|
}
|
|
case GORO_RUN_R0:
|
|
p = StdShrap;
|
|
shrap_xsize = shrap_ysize = 64;
|
|
break;
|
|
case COOLG_RUN_R0:
|
|
p = UpperGore;
|
|
break;
|
|
case RIPPER_RUN_R0:
|
|
p = StdShrap;
|
|
if (pu->spal != 0)
|
|
shrap_xsize = shrap_ysize = 64;
|
|
else
|
|
shrap_xsize = shrap_ysize = 32;
|
|
break;
|
|
case RIPPER2_RUN_R0:
|
|
p = StdShrap;
|
|
if (pu->spal != 0)
|
|
shrap_xsize = shrap_ysize = 64;
|
|
else
|
|
shrap_xsize = shrap_ysize = 32;
|
|
break;
|
|
case SERP_RUN_R0:
|
|
p = StdShrap;
|
|
//return (FALSE);
|
|
break;
|
|
case SUMO_RUN_R0:
|
|
p = StdShrap;
|
|
break;
|
|
case SKEL_RUN_R0:
|
|
p = SkelGore;
|
|
shrap_pal = PALETTE_SKEL_GORE;
|
|
break;
|
|
case HORNET_RUN_R0:
|
|
p = SmallGore;
|
|
shrap_pal = PALETTE_SKEL_GORE;
|
|
break;
|
|
case SKULL_R0:
|
|
case SKULL_R0 + 1:
|
|
p = FlamingGore;
|
|
break;
|
|
case SKULL_SERP:
|
|
return FALSE;
|
|
case BETTY_R0:
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
PlaySound(DIGI_BREAKGLASS,parent,v3df_dontpan|v3df_doppler);
|
|
p = MetalShrap;
|
|
shrap_xsize = shrap_ysize = 10;
|
|
break;
|
|
case ZILLA_RUN_R0:
|
|
p = MetalShrap;
|
|
shrap_xsize = shrap_ysize = 10;
|
|
break;
|
|
case EMP:
|
|
p = EMPShrap;
|
|
shrap_xsize = shrap_ysize = 8;
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
}
|
|
|
|
// second sprite involved
|
|
// most of the time is is the weapon
|
|
if (Secondary >= 0)
|
|
{
|
|
USERp wu = User[Secondary];
|
|
|
|
if (wu->PlayerP && wu->PlayerP->sop_control)
|
|
{
|
|
p = StdShrap;
|
|
}
|
|
else
|
|
switch (wu->ID)
|
|
{
|
|
case PLASMA_FOUNTAIN:
|
|
p = HeartAttackShrap;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hz[Z_TOP] = SPRITEp_TOS(parent); // top
|
|
hz[Z_BOT] = SPRITEp_BOS(parent); // bottom
|
|
hz[Z_MID] = DIV2(hz[0] + hz[2]); // mid
|
|
|
|
for (; p->id; p++)
|
|
{
|
|
if (!p->random_disperse)
|
|
{
|
|
//dang = (2048 / p->num);
|
|
start_ang = NORM_ANGLE(shrap_ang - DIV2(p->ang_range));
|
|
dang = (p->ang_range / p->num);
|
|
}
|
|
|
|
for (i = 0; i < p->num; i++)
|
|
{
|
|
SpriteNum = SpawnSprite(STAT_SKIP4, p->id, p->state, parent->sectnum,
|
|
parent->x, parent->y, hz[p->zlevel], shrap_ang, 512);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
u = User[SpriteNum];
|
|
|
|
if (p->random_disperse)
|
|
{
|
|
sp->ang = shrap_ang + (RANDOM_P2(p->ang_range<<5)>>5) - DIV2(p->ang_range);
|
|
sp->ang = NORM_ANGLE(sp->ang);
|
|
}
|
|
else
|
|
{
|
|
sp->ang = start_ang + (i * dang);
|
|
sp->ang = NORM_ANGLE(sp->ang);
|
|
}
|
|
|
|
// for FastShrap
|
|
u->zchange = labs(u->jump_speed*4) - RANDOM_RANGE(labs(u->jump_speed)*8)*2;
|
|
u->WaitTics = WaitTics + RANDOM_RANGE(WaitTics/2);
|
|
|
|
switch (u->ID)
|
|
{
|
|
case GORE_Drip:
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
case GORE_Lung:
|
|
shrap_xsize = 20;
|
|
shrap_ysize = 20;
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
case GORE_Liver:
|
|
shrap_xsize = 20;
|
|
shrap_ysize = 20;
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
case GORE_SkullCap:
|
|
shrap_xsize = 24;
|
|
shrap_ysize = 24;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
case GORE_Arm:
|
|
shrap_xsize = 21;
|
|
shrap_ysize = 21;
|
|
shrap_bounce = FALSE;
|
|
break;
|
|
case GORE_Head:
|
|
shrap_xsize = 26;
|
|
shrap_ysize = 30;
|
|
shrap_bounce = TRUE;
|
|
break;
|
|
case Vomit1:
|
|
shrap_bounce = FALSE;
|
|
sp->z -= Z(4);
|
|
shrap_xsize = u->sx = 12 + (RANDOM_P2(32<<8)>>8);
|
|
shrap_ysize = u->sy = 12 + (RANDOM_P2(32<<8)>>8);
|
|
u->Counter = (RANDOM_P2(2048<<5)>>5);
|
|
|
|
nx = sintable[NORM_ANGLE(sp->ang+512)]>>6;
|
|
ny = sintable[sp->ang]>>6;
|
|
move_missile(SpriteNum, nx, ny, 0, Z(8), Z(8), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (RANDOM_P2(1024)<700)
|
|
u->ID = 0;
|
|
|
|
break;
|
|
case EMP:
|
|
shrap_bounce = FALSE;
|
|
sp->z -= Z(4);
|
|
//sp->ang = NORM_ANGLE(sp->ang + 1024);
|
|
shrap_xsize = u->sx = 5 + (RANDOM_P2(4<<8)>>8);
|
|
shrap_ysize = u->sy = 5 + (RANDOM_P2(4<<8)>>8);
|
|
break;
|
|
}
|
|
|
|
sp->shade = shrap_shade;
|
|
sp->xrepeat = shrap_xsize;
|
|
sp->yrepeat = shrap_ysize;
|
|
sp->clipdist = 16L >> 2;
|
|
|
|
if (shrap_owner >= 0)
|
|
{
|
|
SetOwner(shrap_owner, SpriteNum);
|
|
}
|
|
|
|
if (shrap_rand_zamt)
|
|
{
|
|
sp->z += Z(RANDOM_RANGE(shrap_rand_zamt) - (shrap_rand_zamt/2));
|
|
}
|
|
|
|
sp->pal = u->spal = shrap_pal;
|
|
|
|
sp->xvel = p->min_vel*2;
|
|
sp->xvel += RANDOM_RANGE(p->max_vel - p->min_vel);
|
|
|
|
u->floor_dist = shrap_floor_dist;
|
|
u->ceiling_dist = shrap_ceiling_dist;
|
|
u->jump_speed = p->min_jspeed;
|
|
u->jump_speed += RANDOM_RANGE(p->max_jspeed - p->min_jspeed);
|
|
u->jump_speed = -u->jump_speed;
|
|
|
|
DoBeginJump(SpriteNum);
|
|
u->jump_grav = jump_grav;
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
|
|
if (!shrap_bounce)
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
DoShrapMove(int16_t SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
|
|
u->ret = move_missile(SpriteNum, u->xchange, u->ychange, 0, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 1
|
|
int
|
|
DoVomit(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
u->Counter = NORM_ANGLE(u->Counter + (30*MISSILEMOVETICS));
|
|
sp->xrepeat = u->sx + ((12 * sintable[NORM_ANGLE(u->Counter+512)]) >> 14);
|
|
sp->yrepeat = u->sy + ((12 * sintable[u->Counter]) >> 14);
|
|
|
|
if (TEST(u->Flags, SPR_JUMPING))
|
|
{
|
|
DoJump(SpriteNum);
|
|
DoJump(SpriteNum);
|
|
DoShrapMove(SpriteNum);
|
|
}
|
|
else if (TEST(u->Flags, SPR_FALLING))
|
|
{
|
|
DoFall(SpriteNum);
|
|
DoFall(SpriteNum);
|
|
DoShrapMove(SpriteNum);
|
|
}
|
|
else
|
|
{
|
|
ChangeState(SpriteNum, s_VomitSplash);
|
|
DoFindGroundPoint(SpriteNum);
|
|
MissileWaterAdjust(SpriteNum);
|
|
sp->z = u->loz;
|
|
u->WaitTics = 60;
|
|
u->sx = sp->xrepeat;
|
|
u->sy = sp->yrepeat;
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
short hit_sprite = NORM_SPRITE(u->ret);
|
|
if (TEST(sprite[hit_sprite].extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
DoDamage(hit_sprite, SpriteNum);
|
|
//KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//if (u->ID)
|
|
// DoDamageTest(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
int
|
|
DoVomit(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
u->Counter = NORM_ANGLE(u->Counter + (30*MISSILEMOVETICS));
|
|
sp->xrepeat = u->sx + ((12 * sintable[NORM_ANGLE(u->Counter+512)]) >> 14);
|
|
sp->yrepeat = u->sy + ((12 * sintable[u->Counter]) >> 14);
|
|
|
|
if (TEST(u->Flags, SPR_JUMPING))
|
|
{
|
|
DoShrapVelocity(SpriteNum);
|
|
}
|
|
else if (TEST(u->Flags, SPR_FALLING))
|
|
{
|
|
DoShrapVelocity(SpriteNum);
|
|
}
|
|
else
|
|
{
|
|
ChangeState(SpriteNum, s_VomitSplash);
|
|
DoFindGroundPoint(SpriteNum);
|
|
MissileWaterAdjust(SpriteNum);
|
|
sp->z = u->loz;
|
|
u->WaitTics = 60;
|
|
u->sx = sp->xrepeat;
|
|
u->sy = sp->yrepeat;
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
short hit_sprite = NORM_SPRITE(u->ret);
|
|
if (TEST(sprite[hit_sprite].extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
DoDamage(hit_sprite, SpriteNum);
|
|
//KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
int
|
|
DoVomitSplash(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
|
|
if ((u->WaitTics-=MISSILEMOVETICS) < 0)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoFastShrapJumpFall(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
sp->x += u->xchange*2;
|
|
sp->y += u->ychange*2;
|
|
sp->z += u->zchange*2;
|
|
|
|
u->WaitTics -= MISSILEMOVETICS;
|
|
if (u->WaitTics <= 0)
|
|
KillSprite(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoTracerShrap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
sp->x += u->xchange;
|
|
sp->y += u->ychange;
|
|
sp->z += u->zchange;
|
|
|
|
u->WaitTics -= MISSILEMOVETICS;
|
|
if (u->WaitTics <= 0)
|
|
KillSprite(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//#define ShrapKillSprite(num) _Shrap_Kill_Sprite(num, __FILE__, __LINE__) //#define DoShrapVelocity(num) _Do_Shrap_Velocity(num, __FILE__, __LINE__)
|
|
|
|
int
|
|
DoShrapJumpFall(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
|
|
if (TEST(u->Flags, SPR_JUMPING))
|
|
{
|
|
DoShrapVelocity(SpriteNum);
|
|
}
|
|
else if (TEST(u->Flags, SPR_FALLING))
|
|
{
|
|
DoShrapVelocity(SpriteNum);
|
|
}
|
|
else
|
|
{
|
|
if (!TEST(u->Flags, SPR_BOUNCE))
|
|
{
|
|
DoShrapVelocity(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
if (u->ID == GORE_Drip)
|
|
ChangeState(SpriteNum, s_GoreFloorSplash);
|
|
else
|
|
ShrapKillSprite(SpriteNum);
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoShrapDamage(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
if (TEST(u->Flags, SPR_JUMPING))
|
|
{
|
|
DoJump(SpriteNum);
|
|
DoShrapMove(SpriteNum);
|
|
}
|
|
else if (TEST(u->Flags, SPR_FALLING))
|
|
{
|
|
DoFall(SpriteNum);
|
|
DoShrapMove(SpriteNum);
|
|
}
|
|
else
|
|
{
|
|
if (!TEST(u->Flags, SPR_BOUNCE))
|
|
{
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->jump_speed = -300;
|
|
sp->xvel >>= 2;
|
|
DoBeginJump(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
if (u->ret)
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
WeaponMoveHit(SpriteNum);
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnBlood(short SpriteNum, short Weapon, short hit_ang, int hit_x, int hit_y, int hit_z)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
SPRITEp np;
|
|
USERp nu, u;
|
|
short i,New;
|
|
|
|
|
|
// state, id, num, zlevel, min_jspeed, max_jspeed, min_vel, max_vel,
|
|
// random_disperse, ang_range;
|
|
|
|
static SHRAP UziBlood[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 1, Z_MID, 100, 250, 10, 20, TRUE, 512}, // 70,200 vels
|
|
//{s_GoreSplash, PLASMA_Drip, 1, Z_BOT, 0, 0, 0, 0, FALSE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP SmallBlood[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 1, Z_TOP, 100, 250, 10, 20, TRUE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP PlasmaFountainBlood[] =
|
|
{
|
|
{s_PlasmaDrip, PLASMA_Drip, 1, Z_TOP, 200, 500, 100, 300, TRUE, 16},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP SomeBlood[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 1, Z_TOP, 100, 250, 10, 20, TRUE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#if 0
|
|
static SHRAP MoreBlood[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 2, Z_TOP, 100, 250, 10, 20, TRUE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
#endif
|
|
|
|
static SHRAP ExtraBlood[] =
|
|
{
|
|
{s_GoreDrip, GORE_Drip, 4, Z_TOP, 100, 250, 10, 20, TRUE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
static SHRAP HariKariBlood[] =
|
|
{
|
|
{s_FastGoreDrip, GORE_Drip, 32, Z_TOP, 200, 650, 70, 100, TRUE, 1024},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
|
|
#if 0
|
|
static SHRAP SwordPowerup[] =
|
|
{
|
|
{s_ElectroShrap, ELECTRO_SHARD, 16, Z_TOP, 75, 200, 70, 150, TRUE, 512},
|
|
{NULL,0,0,0,0,0,0,0,0,0},
|
|
};
|
|
#endif
|
|
|
|
short dang = 0;
|
|
|
|
SHRAPp p = UziBlood;
|
|
short shrap_shade = -15;
|
|
short shrap_xsize = 20, shrap_ysize = 20;
|
|
short retval = TRUE;
|
|
short shrap_pal = PALETTE_DEFAULT;
|
|
short start_ang = 0;
|
|
|
|
u = User[SpriteNum];
|
|
|
|
switch (u->ID)
|
|
{
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
case ZILLA_RUN_R0:
|
|
return 0; // Don't put blood on trashcan
|
|
}
|
|
|
|
// second sprite involved
|
|
// most of the time is the weapon
|
|
if (Weapon >= 0)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
switch (wu->ID)
|
|
{
|
|
case NINJA_RUN_R0: //sword
|
|
// if sprite and weapon are the same it must be HARIII-KARIII
|
|
if (sp == wp)
|
|
{
|
|
p = HariKariBlood;
|
|
hit_ang = sp->ang;
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = SPRITEp_TOS(wp) + DIV16(SPRITEp_SIZE_Z(wp));
|
|
}
|
|
else
|
|
{
|
|
p = ExtraBlood;
|
|
hit_ang = NORM_ANGLE(wp->ang + 1024);
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = SPRITEp_TOS(wp) + DIV4(SPRITEp_SIZE_Z(wp));
|
|
|
|
//ASSERT(wu->PlayerP);
|
|
}
|
|
break;
|
|
case SERP_RUN_R0:
|
|
p = ExtraBlood;
|
|
hit_ang = NORM_ANGLE(wp->ang + 1024);
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = SPRITEp_TOS(sp) + DIV4(SPRITEp_SIZE_Z(sp));
|
|
break;
|
|
case BLADE1:
|
|
case BLADE2:
|
|
case BLADE3:
|
|
case 5011:
|
|
p = SmallBlood;
|
|
hit_ang = NORM_ANGLE(ANG2SPRITE(sp, wp) + 1024);
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = wp->z - DIV2(SPRITEp_SIZE_Z(wp));
|
|
break;
|
|
case STAR1:
|
|
case CROSSBOLT:
|
|
p = SomeBlood;
|
|
hit_ang = NORM_ANGLE(wp->ang + 1024);
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = wp->z;
|
|
break;
|
|
case PLASMA_FOUNTAIN:
|
|
p = PlasmaFountainBlood;
|
|
hit_ang = wp->ang;
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = SPRITEp_TOS(sp) + DIV4(SPRITEp_SIZE_Z(sp));
|
|
break;
|
|
default:
|
|
p = SomeBlood;
|
|
hit_ang = NORM_ANGLE(wp->ang + 1024);
|
|
hit_x = sp->x;
|
|
hit_y = sp->y;
|
|
hit_z = SPRITEp_TOS(wp) + DIV4(SPRITEp_SIZE_Z(wp));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hit_ang = NORM_ANGLE(hit_ang + 1024);
|
|
}
|
|
|
|
for (; p->state; p++)
|
|
{
|
|
if (!p->random_disperse)
|
|
{
|
|
start_ang = NORM_ANGLE(hit_ang - DIV2(p->ang_range)+1024);
|
|
dang = (p->ang_range / p->num);
|
|
}
|
|
|
|
for (i = 0; i < p->num; i++)
|
|
{
|
|
New = SpawnSprite(STAT_SKIP4, p->id, p->state, sp->sectnum,
|
|
hit_x, hit_y, hit_z, hit_ang, 0);
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
switch (nu->ID)
|
|
{
|
|
case ELECTRO_SHARD:
|
|
shrap_xsize = 7;
|
|
shrap_ysize = 7;
|
|
break;
|
|
|
|
case PLASMA_Drip:
|
|
// Don't do central blood splats for every hitscan
|
|
if (RANDOM_P2(1024) < 950)
|
|
{
|
|
np->xrepeat = np->yrepeat = 0;
|
|
}
|
|
if (RANDOM_P2(1024) < 512)
|
|
SET(np->cstat, CSTAT_SPRITE_XFLIP);
|
|
//shrap_xsize = 96;
|
|
//shrap_ysize = 75;
|
|
//shrap_xsize = 10;
|
|
//shrap_ysize = 10;
|
|
break;
|
|
}
|
|
|
|
if (p->random_disperse)
|
|
{
|
|
np->ang = hit_ang + (RANDOM_P2(p->ang_range<<5)>>5) - DIV2(p->ang_range);
|
|
np->ang = NORM_ANGLE(np->ang);
|
|
}
|
|
else
|
|
{
|
|
np->ang = start_ang + (i * dang);
|
|
np->ang = NORM_ANGLE(np->ang);
|
|
}
|
|
|
|
SET(nu->Flags, SPR_BOUNCE);
|
|
|
|
np->shade = shrap_shade;
|
|
np->xrepeat = shrap_xsize;
|
|
np->yrepeat = shrap_ysize;
|
|
np->clipdist = 16L >> 2;
|
|
|
|
np->pal = nu->spal = shrap_pal;
|
|
|
|
np->xvel = p->min_vel;
|
|
np->xvel += RANDOM_RANGE(p->max_vel - p->min_vel);
|
|
|
|
// special case
|
|
// blood coming off of actors should have the acceleration of the actor
|
|
// so add it in
|
|
np->xvel += sp->xvel;
|
|
|
|
nu->ceiling_dist = nu->floor_dist = Z(2);
|
|
nu->jump_speed = p->min_jspeed;
|
|
nu->jump_speed += RANDOM_RANGE(p->max_jspeed - p->min_jspeed);
|
|
nu->jump_speed = -nu->jump_speed;
|
|
|
|
//setspritez(New, (vec3_t *)np);
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
|
|
// for FastShrap
|
|
nu->zchange = labs(nu->jump_speed*4) - RANDOM_RANGE(labs(nu->jump_speed)*8);
|
|
nu->WaitTics = 64 + RANDOM_P2(32);
|
|
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
|
|
DoBeginJump(New);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
SWBOOL
|
|
VehicleMoveHit(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp cp;
|
|
SECTOR_OBJECTp sop;
|
|
SECTOR_OBJECTp hsop;
|
|
SWBOOL TestKillSectorObject(SECTOR_OBJECTp);
|
|
short controller;
|
|
|
|
if (!u->ret)
|
|
return FALSE;
|
|
|
|
sop = u->sop_parent;
|
|
|
|
// sprite controlling sop
|
|
cp = sop->controller;
|
|
controller = cp - sprite;
|
|
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SECTOR:
|
|
{
|
|
short hit_sect = NORM_SECTOR(u->ret);
|
|
SECTORp sectp = §or[hit_sect];
|
|
|
|
if (TEST(sectp->extra, SECTFX_SECTOR_OBJECT))
|
|
{
|
|
// shouldn't ever really happen
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case HIT_SPRITE:
|
|
{
|
|
short hit_sprite = NORM_SPRITE(u->ret);
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hit_sprite, u->ID);
|
|
return TRUE;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
if (hit_sprite != cp->owner)
|
|
{
|
|
DoDamage(hit_sprite, controller);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hsp->statnum == STAT_MINE_STUCK)
|
|
{
|
|
DoDamage(hit_sprite, SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall = NORM_WALL(u->ret);
|
|
WALLp wph = &wall[hit_wall];
|
|
|
|
if (TEST(wph->extra, WALLFX_SECTOR_OBJECT))
|
|
{
|
|
// sector object collision
|
|
if ((hsop = DetectSectorObjectByWall(wph)))
|
|
{
|
|
#if 1
|
|
SopDamage(hsop, sop->ram_damage);
|
|
if (hsop->max_damage <= 0)
|
|
SopCheckKill(hsop);
|
|
else
|
|
DoSpawnSpotsForDamage(hsop->match_event);
|
|
#else
|
|
// drivable damage is DIE_HARD type
|
|
if (hsop->max_damage != -9999 && sop->ram_damage)
|
|
{
|
|
hsop->max_damage -= sop->ram_damage;
|
|
if (hsop->max_damage <= 0)
|
|
{
|
|
TestKillSectorObject(hsop);
|
|
}
|
|
else
|
|
{
|
|
DoSpawnSpotsForDamage(hsop->match_event);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
SWBOOL
|
|
WeaponMoveHit(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
|
|
|
|
if (!u->ret)
|
|
return FALSE;
|
|
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_PLAX_WALL:
|
|
SetSuicide(SpriteNum);
|
|
return TRUE;
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
short hit_sect;
|
|
SECTORp sectp;
|
|
SECTOR_OBJECTp sop;
|
|
|
|
hit_sect = NORM_SECTOR(u->ret);
|
|
sectp = §or[hit_sect];
|
|
|
|
ASSERT(sectp->extra != -1);
|
|
|
|
// hit floor - closer to floor than ceiling
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
// hit a floor sprite
|
|
if (u->lo_sp)
|
|
{
|
|
|
|
if (u->lo_sp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(SpriteNum, -1, u->lo_sp - sprite))
|
|
return TRUE;
|
|
//DoMatchEverything(NULL, u->lo_sp->hitag, -1);
|
|
//return(TRUE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (SectUser[hit_sect] && SectUser[hit_sect]->depth > 0)
|
|
{
|
|
SpawnSplash(SpriteNum);
|
|
//SetSuicide(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
// hit ceiling
|
|
else
|
|
{
|
|
// hit a floor sprite
|
|
if (u->hi_sp)
|
|
{
|
|
if (u->hi_sp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(SpriteNum, -1, u->hi_sp - sprite))
|
|
return TRUE;
|
|
//DoMatchEverything(NULL, u->hi_sp->hitag, -1);
|
|
//return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (TEST(sectp->extra, SECTFX_SECTOR_OBJECT))
|
|
{
|
|
if ((sop = DetectSectorObject(sectp)))
|
|
{
|
|
//if (sop->max_damage != -9999)
|
|
DoDamage(sop->sp_child - sprite, SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (TEST(sectp->ceilingstat, CEILING_STAT_PLAX) && sectp->ceilingpicnum != FAF_MIRROR_PIC)
|
|
{
|
|
if (labs(sp->z - sectp->ceilingz) < SPRITEp_SIZE_Z(sp))
|
|
{
|
|
SetSuicide(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case HIT_SPRITE:
|
|
{
|
|
SPRITEp hsp;
|
|
USERp hu;
|
|
short hit_sprite;
|
|
|
|
hit_sprite = NORM_SPRITE(u->ret);
|
|
hsp = &sprite[hit_sprite];
|
|
hu = User[hit_sprite];
|
|
|
|
if (hsp->statnum == STAT_ENEMY)
|
|
{
|
|
////DSPRINTF(ds, "Bullet Hit Monster %d, stat %d ", hsp-sprite, (short)hsp->statnum);
|
|
//MONO_PRINT(ds);
|
|
}
|
|
|
|
ASSERT(hsp->extra != -1);
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hit_sprite, u->ID);
|
|
return TRUE;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
// make sure you didn't hit the owner of the missile
|
|
//if (sp->owner != -1 && hit_sprite != sp->owner)
|
|
if (hit_sprite != sp->owner)
|
|
{
|
|
if (u->ID == STAR1)
|
|
{
|
|
extern STATE s_TrashCanPain[];
|
|
switch (hu->ID)
|
|
{
|
|
case TRASHCAN:
|
|
PlaySound(DIGI_TRASHLID, sp, v3df_none);
|
|
PlaySound(DIGI_STARCLINK, sp, v3df_none);
|
|
if (hu->WaitTics <= 0)
|
|
{
|
|
hu->WaitTics = SEC(2);
|
|
ChangeState(hit_sprite,s_TrashCanPain);
|
|
}
|
|
break;
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case ZILLA_RUN_R0:
|
|
case 623:
|
|
{
|
|
PlaySound(DIGI_STARCLINK, sp, v3df_none);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
DoDamage(hit_sprite, SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hsp->statnum == STAT_MINE_STUCK)
|
|
{
|
|
DoDamage(hit_sprite, SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(SpriteNum, -1, hit_sprite))
|
|
return TRUE;
|
|
//DoMatchEverything(NULL, hsp->hitag, -1);
|
|
//return(TRUE);
|
|
}
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
if (hsp->lotag || hsp->hitag)
|
|
{
|
|
ShootableSwitch(hit_sprite);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
hitdata_t hitinfo = { { 0, 0, 0 }, -2, (int16_t)NORM_WALL(u->ret), -2 };
|
|
WALLp wph = &wall[hitinfo.wall];
|
|
SECTOR_OBJECTp sop;
|
|
|
|
ASSERT(wph->extra != -1);
|
|
|
|
if (TEST(wph->extra, WALLFX_SECTOR_OBJECT))
|
|
{
|
|
if ((sop = DetectSectorObjectByWall(wph)))
|
|
{
|
|
if (sop->max_damage != -999)
|
|
DoDamage(sop->sp_child - sprite, SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (wph->lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], sp->x, sp->y, sp->z, sp->ang, u->ID);
|
|
u->ret = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
// clipmove does not correctly return the sprite for WALL sprites
|
|
// on walls, so look with hitscan
|
|
|
|
hitscan((vec3_t *)sp, sp->sectnum, // Start position
|
|
sintable[NORM_ANGLE(sp->ang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(sp->ang)], // Y vector of 3D ang
|
|
sp->zvel, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
SPRITEp hsp = &sprite[hitinfo.sprite];
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(SpriteNum, -1, hitinfo.sprite))
|
|
return TRUE;
|
|
}
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
if (hsp->lotag || hsp->hitag)
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoUziSmoke(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
//if (sp->picnum != NULL)
|
|
// DoDamageTest(SpriteNum);
|
|
sp->z -= 200; // !JIM! Make them float up
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoShotgunSmoke(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
//if (sp->picnum != NULL)
|
|
// DoDamageTest(SpriteNum);
|
|
sp->z -= 200; // !JIM! Make them float up
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoMineSpark(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
if (sp->picnum != 0)
|
|
{
|
|
DoDamageTest(SpriteNum);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoFireballFlames(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum],ap;
|
|
USERp u = User[SpriteNum];
|
|
SWBOOL jumping = FALSE;
|
|
|
|
// if no owner then stay where you are
|
|
if (u->Attach >= 0)
|
|
{
|
|
ap = &sprite[u->Attach];
|
|
|
|
sp->x = ap->x;
|
|
sp->y = ap->y;
|
|
|
|
sp->z = DIV2(SPRITEp_TOS(ap) + SPRITEp_BOS(ap));
|
|
|
|
if (TEST(ap->extra, SPRX_BURNABLE))
|
|
{
|
|
if (MOD2(u->Counter2) == 0)
|
|
{
|
|
ap->shade++;
|
|
if (ap->shade > 10)
|
|
ap->shade = 10;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TEST(u->Flags, SPR_JUMPING))
|
|
{
|
|
DoJump(SpriteNum);
|
|
jumping = TRUE;
|
|
//u->ret = move_missile(SpriteNum, dax, day, daz, Z(16), Z(16), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
}
|
|
else if (TEST(u->Flags, SPR_FALLING))
|
|
{
|
|
DoFall(SpriteNum);
|
|
jumping = TRUE;
|
|
//u->ret = move_missile(SpriteNum, dax, day, daz, Z(16), Z(16), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
}
|
|
else
|
|
{
|
|
if (SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth > 0)
|
|
{
|
|
if (labs(sector[sp->sectnum].floorz - sp->z) <= Z(4))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (TestDontStickSector(sp->sectnum))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!jumping)
|
|
{
|
|
if ((u->WaitTics += MISSILEMOVETICS) > 4 * 120)
|
|
{
|
|
// shrink and go away
|
|
sp->xrepeat--;
|
|
sp->yrepeat--;
|
|
|
|
if (((signed char)sp->xrepeat) == 0)
|
|
{
|
|
if (u->Attach >= 0)
|
|
User[u->Attach]->flame = -1;
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// grow until the right size
|
|
if (sp->xrepeat <= u->Counter)
|
|
{
|
|
sp->xrepeat += 3;
|
|
sp->yrepeat += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
u->Counter2++;
|
|
if (u->Counter2 > 9)
|
|
{
|
|
u->Counter2 = 0;
|
|
DoFlamesDamageTest(SpriteNum);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoBreakFlames(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SWBOOL jumping = FALSE;
|
|
|
|
if (TEST(u->Flags, SPR_JUMPING))
|
|
{
|
|
DoJump(SpriteNum);
|
|
jumping = TRUE;
|
|
}
|
|
else if (TEST(u->Flags, SPR_FALLING))
|
|
{
|
|
DoFall(SpriteNum);
|
|
jumping = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth > 0)
|
|
{
|
|
if (labs(sector[sp->sectnum].floorz - sp->z) <= Z(4))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (TestDontStickSector(sp->sectnum))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!jumping)
|
|
{
|
|
if ((u->WaitTics += MISSILEMOVETICS) > 4 * 120)
|
|
{
|
|
// shrink and go away
|
|
sp->xrepeat--;
|
|
sp->yrepeat--;
|
|
|
|
if (((signed char)sp->xrepeat) == 0)
|
|
{
|
|
if (u->Attach >= 0)
|
|
User[u->Attach]->flame = -1;
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// grow until the right size
|
|
if (sp->xrepeat <= u->Counter)
|
|
{
|
|
sp->xrepeat += 3;
|
|
sp->yrepeat += 3;
|
|
}
|
|
|
|
if (u->WaitTics + MISSILEMOVETICS > 4 * 120)
|
|
{
|
|
SpawnBreakStaticFlames(SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
u->Counter2++;
|
|
if (u->Counter2 > 9)
|
|
{
|
|
u->Counter2 = 0;
|
|
DoFlamesDamageTest(SpriteNum);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SetSuicide(short SpriteNum)
|
|
{
|
|
#if 1
|
|
USERp u = User[SpriteNum];
|
|
|
|
if (u != nullptr)
|
|
{
|
|
SET(u->Flags, SPR_SUICIDE);
|
|
u->RotNum = 0;
|
|
}
|
|
ChangeState(SpriteNum, s_Suicide);
|
|
#else
|
|
// this will NOT work because
|
|
// TRAVERSE_SPRITE_STAT(headspritestat[STAT_MISSILE], i, nexti)
|
|
// nexti will still be valid but will be on a different list
|
|
// and will have a different nextspritestat[nexti] result
|
|
changespritestat(SpriteNum, STAT_SUICIDE);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoActorScale(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
u->scale_speed = 70;
|
|
u->scale_value = sp->xrepeat << 8;
|
|
u->scale_tgt = sp->xrepeat + 25;
|
|
|
|
if (u->scale_tgt > 256)
|
|
{
|
|
u->scale_speed = 0;
|
|
u->scale_tgt = 256;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoRipperGrow(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
u->scale_speed = 70;
|
|
u->scale_value = sp->xrepeat << 8;
|
|
u->scale_tgt = sp->xrepeat + 20;
|
|
|
|
if (u->scale_tgt > 128)
|
|
{
|
|
u->scale_speed = 0;
|
|
u->scale_tgt = 128;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void UpdateSinglePlayKills(short SpriteNum)
|
|
{
|
|
// single play and coop kill count
|
|
if (gNet.MultiGameType != MULTI_GAME_COMMBAT)
|
|
{
|
|
ASSERT(User[SpriteNum]);
|
|
|
|
if (TEST(User[SpriteNum]->Flags, SPR_SUICIDE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (User[SpriteNum]->ID)
|
|
{
|
|
case COOLIE_RUN_R0:
|
|
case NINJA_RUN_R0:
|
|
case NINJA_CRAWL_R0:
|
|
case GORO_RUN_R0:
|
|
case 1441:
|
|
case COOLG_RUN_R0:
|
|
case EEL_RUN_R0:
|
|
case SUMO_RUN_R0:
|
|
case ZILLA_RUN_R0:
|
|
//case TOILETGIRL_R0:
|
|
//case WASHGIRL_R0:
|
|
//case BUNNY_RUN_R0:
|
|
case RIPPER_RUN_R0:
|
|
case RIPPER2_RUN_R0:
|
|
case SERP_RUN_R0:
|
|
case LAVA_RUN_R0:
|
|
case SKEL_RUN_R0:
|
|
case HORNET_RUN_R0:
|
|
case GIRLNINJA_RUN_R0:
|
|
case SKULL_R0:
|
|
case BETTY_R0:
|
|
// always give kills to the first player
|
|
Player->Kills++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
ActorChooseDeath(short SpriteNum, short Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
ASSERT(u);
|
|
|
|
if (u->Health > 0)
|
|
return FALSE;
|
|
|
|
UpdateSinglePlayKills(SpriteNum);
|
|
|
|
ASSERT(Weapon >= 0);
|
|
|
|
#if 0
|
|
if (Weapon < 0)
|
|
{
|
|
switch (Weapon)
|
|
{
|
|
default:
|
|
DoActorDie(SpriteNum, Weapon);
|
|
UpdateSinglePlayKills(SpriteNum);
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (u->Attrib)
|
|
PlaySpriteSound(SpriteNum,attr_die,v3df_follow);
|
|
|
|
switch (u->ID)
|
|
{
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
case TOILETGIRL_R0:
|
|
case WASHGIRL_R0:
|
|
case CARGIRL_R0:
|
|
case MECHANICGIRL_R0:
|
|
case SAILORGIRL_R0:
|
|
case PRUNEGIRL_R0:
|
|
case TRASHCAN:
|
|
case GORO_RUN_R0:
|
|
case RIPPER2_RUN_R0:
|
|
break;
|
|
case BUNNY_RUN_R0:
|
|
{
|
|
extern short Bunny_Count;
|
|
Bunny_Count--; // Bunny died, decrease the population
|
|
}
|
|
break;
|
|
default:
|
|
ActorCoughItem(SpriteNum);
|
|
//UpdateSinglePlayKills(SpriteNum);
|
|
break;
|
|
}
|
|
|
|
switch (u->ID)
|
|
{
|
|
case BETTY_R0:
|
|
{
|
|
DoBettyBeginDeath(SpriteNum);
|
|
break;
|
|
}
|
|
case SKULL_R0:
|
|
{
|
|
DoSkullBeginDeath(SpriteNum);
|
|
break;
|
|
}
|
|
case TOILETGIRL_R0:
|
|
case WASHGIRL_R0:
|
|
case CARGIRL_R0:
|
|
case MECHANICGIRL_R0:
|
|
case SAILORGIRL_R0:
|
|
case PRUNEGIRL_R0:
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
{
|
|
if ((u->ID == TOILETGIRL_R0 ||
|
|
u->ID == CARGIRL_R0 || u->ID == MECHANICGIRL_R0 || u->ID == SAILORGIRL_R0 || u->ID == PRUNEGIRL_R0 ||
|
|
u->ID == WASHGIRL_R0) && wu->ID == NINJA_RUN_R0 && wu->PlayerP)
|
|
{
|
|
PLAYERp pp = wu->PlayerP;
|
|
if (pp && !TEST(pp->Flags, PF_DIVING)) // JBF: added null test
|
|
pp->Bloody = TRUE;
|
|
PlaySound(DIGI_TOILETGIRLSCREAM, sp, v3df_none);
|
|
}
|
|
if (SpawnShrap(SpriteNum, Weapon))
|
|
SetSuicide(SpriteNum);
|
|
break;
|
|
}
|
|
|
|
// These are player zombies
|
|
case ZOMBIE_RUN_R0:
|
|
InitBloodSpray(SpriteNum,TRUE,105);
|
|
InitBloodSpray(SpriteNum,TRUE,105);
|
|
if (SpawnShrap(SpriteNum, Weapon))
|
|
SetSuicide(SpriteNum);
|
|
break;
|
|
|
|
default:
|
|
|
|
switch (wu->ID)
|
|
{
|
|
case NINJA_RUN_R0: //sword
|
|
if (wu->PlayerP)
|
|
{
|
|
PLAYERp pp = wu->PlayerP;
|
|
|
|
if (wu->WeaponNum == WPN_FIST && STD_RANDOM_RANGE(1000)>500 && pp == Player+myconnectindex)
|
|
{
|
|
char choosesnd = 0;
|
|
|
|
choosesnd=STD_RANDOM_RANGE(6);
|
|
|
|
if (choosesnd == 0)
|
|
PlayerSound(DIGI_KUNGFU, v3df_follow|v3df_dontpan,pp);
|
|
else if (choosesnd == 1)
|
|
PlayerSound(DIGI_PAYINGATTENTION, v3df_follow|v3df_dontpan,pp);
|
|
else if (choosesnd == 2)
|
|
PlayerSound(DIGI_EATTHIS, v3df_follow|v3df_dontpan,pp);
|
|
else if (choosesnd == 3)
|
|
PlayerSound(DIGI_TAUNTAI4, v3df_follow|v3df_dontpan,pp);
|
|
else if (choosesnd == 4)
|
|
PlayerSound(DIGI_TAUNTAI5, v3df_follow|v3df_dontpan,pp);
|
|
else if (choosesnd == 5)
|
|
PlayerSound(DIGI_HOWYOULIKEMOVE, v3df_follow|v3df_dontpan,pp);
|
|
//PlayerSound(TauntAIVocs[choosesnd],&pp->posx,
|
|
// &pp->posy,&pp->posy,v3df_dontpan|v3df_follow,pp);
|
|
}
|
|
else if (wu->WeaponNum == WPN_SWORD && STD_RANDOM_RANGE(1000)>500 && pp == Player+myconnectindex)
|
|
{
|
|
short choose_snd;
|
|
|
|
choose_snd = STD_RANDOM_RANGE(1000);
|
|
if (choose_snd > 750)
|
|
PlayerSound(DIGI_SWORDGOTU1, v3df_follow|v3df_dontpan,pp);
|
|
else if (choose_snd > 575)
|
|
PlayerSound(DIGI_SWORDGOTU2, v3df_follow|v3df_dontpan,pp);
|
|
else if (choose_snd > 250)
|
|
PlayerSound(DIGI_SWORDGOTU3, v3df_follow|v3df_dontpan,pp);
|
|
else
|
|
PlayerSound(DIGI_CANBEONLYONE, v3df_follow|v3df_dontpan,pp);
|
|
}
|
|
if (!TEST(pp->Flags, PF_DIVING))
|
|
pp->Bloody = TRUE;
|
|
}
|
|
|
|
if (u->WeaponNum == WPN_FIST)
|
|
DoActorDie(SpriteNum, Weapon);
|
|
else if (u->ID == NINJA_RUN_R0 || RANDOM_RANGE(1000) < 500)
|
|
DoActorDie(SpriteNum, Weapon);
|
|
else
|
|
{
|
|
// Can't gib bosses!
|
|
if (u->ID == SERP_RUN_R0 || u->ID == SUMO_RUN_R0 || u->ID == ZILLA_RUN_R0)
|
|
{
|
|
DoActorDie(SpriteNum, Weapon);
|
|
break;
|
|
}
|
|
|
|
// Gib out the ones you can't cut in half
|
|
// Blood fountains
|
|
InitBloodSpray(SpriteNum,TRUE,-1);
|
|
|
|
if (SpawnShrap(SpriteNum, Weapon))
|
|
{
|
|
SetSuicide(SpriteNum);
|
|
}
|
|
else
|
|
DoActorDie(SpriteNum, Weapon);
|
|
|
|
}
|
|
break;
|
|
#if 0
|
|
case PLASMA_FOUNTAIN:
|
|
if (SpawnShrap(SpriteNum, Weapon))
|
|
SetSuicide(SpriteNum);
|
|
else
|
|
DoActorDie(SpriteNum, Weapon);
|
|
break;
|
|
#endif
|
|
case BOLT_THINMAN_R0:
|
|
case BOLT_THINMAN_R1:
|
|
case BOLT_THINMAN_R2:
|
|
case NAP_EXP:
|
|
case BOLT_EXP:
|
|
case TANK_SHELL_EXP:
|
|
case SECTOR_EXP:
|
|
case FIREBALL_EXP:
|
|
case GRENADE_EXP:
|
|
case MINE_EXP:
|
|
case SKULL_R0:
|
|
case BETTY_R0:
|
|
#if 0
|
|
if (RANDOM_P2(1024) < 256 && wu->Radius != NUKE_RADIUS)
|
|
{
|
|
if (wu->ID == BOLT_THINMAN_R1 && wu->Radius == RAIL_RADIUS)
|
|
{
|
|
SpawnShrapX(Weapon); // Do rail gun shrap
|
|
}
|
|
DoActorDie(SpriteNum, Weapon);
|
|
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int choosesnd = 0;
|
|
|
|
// For the Nuke, do residual radiation if he gibs
|
|
if (wu->Radius == NUKE_RADIUS)
|
|
SpawnFireballFlames(SpriteNum, -1);
|
|
|
|
// Random chance of taunting the AI's here
|
|
if (RANDOM_RANGE(1000) > 400)
|
|
{
|
|
PLAYERp pp;
|
|
|
|
if (wp && wp->owner >= 0)
|
|
{
|
|
if (User[wp->owner]->PlayerP)
|
|
{
|
|
|
|
pp = User[wp->owner]->PlayerP;
|
|
|
|
|
|
ASSERT(pp >= Player && pp <= Player+MAX_SW_PLAYERS);
|
|
choosesnd=STD_RANDOM_RANGE(MAX_TAUNTAI<<8)>>8;
|
|
|
|
if (pp && pp == Player+myconnectindex)
|
|
PlayerSound(TauntAIVocs[choosesnd],v3df_dontpan|v3df_follow,pp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// These guys cough items only if gibbed
|
|
if (u->ID == GORO_RUN_R0 || u->ID == RIPPER2_RUN_R0)
|
|
ActorCoughItem(SpriteNum);
|
|
|
|
// Blood fountains
|
|
InitBloodSpray(SpriteNum,TRUE,-1);
|
|
|
|
// Bosses do not gib
|
|
if (u->ID == SERP_RUN_R0 || u->ID == SUMO_RUN_R0 || u->ID == ZILLA_RUN_R0)
|
|
{
|
|
DoActorDie(SpriteNum, Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
if (SpawnShrap(SpriteNum, Weapon))
|
|
{
|
|
SetSuicide(SpriteNum);
|
|
}
|
|
else
|
|
DoActorDie(SpriteNum, Weapon);
|
|
|
|
}
|
|
|
|
break;
|
|
default:
|
|
DoActorDie(SpriteNum, Weapon);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
int
|
|
ActorHealth(short SpriteNum, short amt)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
extern SWBOOL FinishAnim;
|
|
|
|
if (u->ID == TRASHCAN && amt > -75)
|
|
{
|
|
u->LastDamage = 100;
|
|
return TRUE;
|
|
}
|
|
|
|
SET(u->Flags, SPR_ATTACKED);
|
|
|
|
u->Health += amt;
|
|
|
|
if (u->ID == SERP_RUN_R0 && sp->pal != 16 && currentLevel->levelNumber == 4)
|
|
{
|
|
if (u->Health < u->MaxHealth/2)
|
|
{
|
|
FinishAnim = ANIM_SERP;
|
|
ChangeLevel(nullptr, -1);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (u->ID == SUMO_RUN_R0 && currentLevel->levelNumber == 11)
|
|
{
|
|
if (u->Health <= 0)
|
|
{
|
|
FinishTimer = 7*120;
|
|
FinishAnim = ANIM_SUMO;
|
|
}
|
|
}
|
|
|
|
if (u->ID == ZILLA_RUN_R0 && currentLevel->levelNumber == 20)
|
|
{
|
|
if (u->Health <= 0)
|
|
//if (u->Health < u->MaxHealth)
|
|
{
|
|
FinishTimer = 15*120;
|
|
FinishAnim = ANIM_ZILLA;
|
|
}
|
|
}
|
|
|
|
if (u->Attrib && RANDOM_P2(1024) > 850)
|
|
PlaySpriteSound(SpriteNum,attr_pain,v3df_follow|v3df_dontpan);
|
|
|
|
// keep track of the last damage
|
|
if (amt < 0)
|
|
u->LastDamage = -amt;
|
|
|
|
// Do alternate Death2 if it exists
|
|
if (u->ActorActionSet && u->ActorActionSet->Death2) // JBF: added null check
|
|
{
|
|
#define DEATH2_HEALTH_VALUE 15
|
|
|
|
if (u->Health <= DEATH2_HEALTH_VALUE)
|
|
{
|
|
// If he's dead, possibly choose a special death type
|
|
#if 1 // Problematic code, REMOVED.
|
|
switch (u->ID)
|
|
{
|
|
case NINJA_RUN_R0:
|
|
{
|
|
extern STATEp sg_NinjaGrabThroat[];
|
|
extern STATEp sg_NinjaHariKari[];
|
|
|
|
if (TEST(u->Flags2, SPR2_DYING)) return TRUE;
|
|
if (TEST(u->Flags, SPR_FALLING | SPR_JUMPING | SPR_CLIMBING)) return TRUE;
|
|
|
|
if (!TEST(u->Flags2, SPR2_DYING))
|
|
{
|
|
short rnd;
|
|
|
|
rnd = RANDOM_P2(1024<<4)>>4;
|
|
if (rnd < 950)
|
|
return TRUE;
|
|
SET(u->Flags2, SPR2_DYING); // Only let it check this once!
|
|
u->WaitTics = SEC(1) + SEC(RANDOM_RANGE(2));
|
|
u->Health = 60;
|
|
PlaySound(DIGI_NINJACHOKE, sp, v3df_follow);
|
|
InitPlasmaFountain(NULL, sp);
|
|
InitBloodSpray(SpriteNum,FALSE,105);
|
|
sp->ang = NORM_ANGLE(getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y) + 1024);
|
|
RESET(sp->cstat, CSTAT_SPRITE_YFLIP);
|
|
if (sw_ninjahack)
|
|
NewStateGroup(SpriteNum, sg_NinjaHariKari);
|
|
else
|
|
NewStateGroup(SpriteNum, sg_NinjaGrabThroat);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
SopDamage(SECTOR_OBJECTp sop, short amt)
|
|
{
|
|
SPRITEp sp = sop->sp_child;
|
|
USERp u = User[sp - sprite];
|
|
|
|
// does not have damage
|
|
if (sop->max_damage == -9999)
|
|
return FALSE;
|
|
|
|
sop->max_damage += amt;
|
|
|
|
// keep track of the last damage
|
|
if (amt < 0)
|
|
u->LastDamage = -amt;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
SopCheckKill(SECTOR_OBJECTp sop)
|
|
{
|
|
SWBOOL killed = FALSE;
|
|
|
|
if (TEST(sop->flags, SOBJ_BROKEN))
|
|
return FALSE;
|
|
|
|
// does not have damage
|
|
if (sop->max_damage == -9999)
|
|
return killed;
|
|
|
|
if (sop->max_damage <= 0)
|
|
{
|
|
// returns whether so was collapsed(killed)
|
|
killed = TestKillSectorObject(sop);
|
|
if (!killed)
|
|
{
|
|
VehicleSetSmoke(sop, SpawnVehicleSmoke);
|
|
SET(sop->flags, SOBJ_BROKEN);
|
|
}
|
|
}
|
|
|
|
return killed;
|
|
}
|
|
|
|
int
|
|
ActorPain(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
|
|
//if (u->LastDamage < u->PainThreshold) // This doesn't work well at all because of
|
|
// uzi/shotgun damages
|
|
switch (u->ID)
|
|
{
|
|
case TOILETGIRL_R0:
|
|
case WASHGIRL_R0:
|
|
case CARGIRL_R0:
|
|
case MECHANICGIRL_R0:
|
|
case SAILORGIRL_R0:
|
|
case PRUNEGIRL_R0:
|
|
u->FlagOwner = 1;
|
|
break;
|
|
}
|
|
|
|
if (RANDOM_RANGE(1000) < 875 || u->WaitTics > 0)
|
|
return FALSE;
|
|
|
|
if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
|
|
{
|
|
if (u->ActorActionSet && u->ActorActionSet->Pain)
|
|
{
|
|
ActorLeaveTrack(SpriteNum);
|
|
u->WaitTics = 60;
|
|
NewStateGroup(SpriteNum, u->ActorActionSet->Pain);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
ActorPainPlasma(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
|
|
if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING | SPR_ELECTRO_TOLERANT))
|
|
{
|
|
if (u->ActorActionSet && u->ActorActionSet->Pain)
|
|
{
|
|
u->WaitTics = PLASMA_FOUNTAIN_TIME;
|
|
NewStateGroup(SpriteNum, u->ActorActionSet->Pain);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
u->Vis = PLASMA_FOUNTAIN_TIME;
|
|
InitActorPause(SpriteNum);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
ActorStdMissile(short SpriteNum, short Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
ASSERT(Weapon >= 0);
|
|
|
|
// Attack the player that is attacking you
|
|
// Only if hes still alive
|
|
if (wp->owner >= 0)
|
|
{
|
|
// Commented out line will make actors attack each other, it's been removed as feature
|
|
// if (User[wp->owner]->PlayerP || User[wp->owner]->SpriteP)
|
|
if (User[wp->owner]->PlayerP && sp->owner != wp->owner)
|
|
{
|
|
u->tgt_sp = &sprite[wp->owner];
|
|
}
|
|
}
|
|
|
|
// Reset the weapons target before dying
|
|
if (wu->WpnGoal >= 0)
|
|
{
|
|
// attempt to see if it was killed
|
|
ASSERT(sprite[wu->WpnGoal].sectnum >= 0);
|
|
ASSERT(User[Weapon]);
|
|
// what if the target has been killed? This will crash!
|
|
RESET(User[wu->WpnGoal]->Flags, SPR_TARGETED);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
int
|
|
ActorDamageSlide(short SpriteNum, short damage, short ang)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
int slide_vel,slide_dec;
|
|
|
|
if (TEST(u->Flags, SPR_CLIMBING))
|
|
return FALSE;
|
|
|
|
damage = labs(damage);
|
|
|
|
if (!damage)
|
|
return FALSE;
|
|
|
|
if (damage <= 10)
|
|
{
|
|
DoActorBeginSlide(SpriteNum, ang, 64, 5);
|
|
return TRUE;
|
|
}
|
|
else if (damage <= 20)
|
|
{
|
|
DoActorBeginSlide(SpriteNum, ang, 128, 5);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
slide_vel = (damage * 6) - (u->MaxHealth);
|
|
|
|
//DSPRINTF(ds,"slide_vel = %ld",slide_vel);
|
|
MONO_PRINT(ds);
|
|
|
|
if (slide_vel < -1000) slide_vel = -1000;
|
|
slide_dec = 5;
|
|
|
|
DoActorBeginSlide(SpriteNum, ang, slide_vel, slide_dec);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
int
|
|
PlayerDamageSlide(PLAYERp pp, short damage, short ang)
|
|
{
|
|
int slide_vel;
|
|
|
|
damage = labs(damage);
|
|
|
|
if (!damage)
|
|
return FALSE;
|
|
|
|
if (damage <= 5)
|
|
{
|
|
//nudge
|
|
//pp->slide_xvect = MOVEx(4, ang)<<15;
|
|
//pp->slide_yvect = MOVEy(4, ang)<<15;
|
|
//return(TRUE);
|
|
return FALSE;
|
|
}
|
|
else if (damage <= 10)
|
|
{
|
|
//nudge
|
|
pp->slide_xvect = MOVEx(16, ang)<<15;
|
|
pp->slide_yvect = MOVEy(16, ang)<<15;
|
|
return TRUE;
|
|
}
|
|
else if (damage <= 20)
|
|
{
|
|
//bigger nudge
|
|
pp->slide_xvect = MOVEx(64, ang)<<15;
|
|
pp->slide_yvect = MOVEy(64, ang)<<15;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
slide_vel = (damage * 6);
|
|
|
|
pp->slide_xvect = MOVEx(slide_vel, ang)<<15;
|
|
pp->slide_yvect = MOVEy(slide_vel, ang)<<15;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
GetDamage(short SpriteNum, short Weapon, short DamageNdx)
|
|
{
|
|
DAMAGE_DATAp d = &DamageData[DamageNdx];
|
|
|
|
ASSERT(SpriteNum >= 0 && Weapon >= 0);
|
|
|
|
// if ndx does radius
|
|
if (d->radius > 0)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
SPRITEp wp = &sprite[Weapon];
|
|
int dist,a,b,c;
|
|
int damage_per_pixel, damage_force, damage_amt;
|
|
|
|
|
|
DISTANCE(wp->x,wp->y,sp->x,sp->y,dist,a,b,c);
|
|
|
|
// take off the box around the player or else you'll never get
|
|
// the max_damage;
|
|
dist -= ((int)sp->clipdist)<<(2);
|
|
|
|
if (dist < 0) dist = 0;
|
|
|
|
if ((unsigned)dist < d->radius)
|
|
{
|
|
damage_per_pixel = IntToFixed(d->damage_hi)/d->radius;
|
|
|
|
//the closer your distance is to 0 the more damage
|
|
damage_force = (d->radius - dist);
|
|
damage_amt = -FixedToInt(damage_force * damage_per_pixel);
|
|
|
|
//return(damage_amt);
|
|
// formula: damage_amt = 75% + random(25%)
|
|
return DIV2(damage_amt) + DIV4(damage_amt) + RANDOM_RANGE(DIV4(damage_amt));
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -(d->damage_lo + RANDOM_RANGE(d->damage_hi - d->damage_lo));
|
|
}
|
|
|
|
int
|
|
RadiusGetDamage(short SpriteNum, short Weapon, int max_damage)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
int dist,a,b,c;
|
|
int damage_per_pixel, damage_force, damage_amt;
|
|
|
|
max_damage = labs(max_damage);
|
|
|
|
DISTANCE(wp->x,wp->y,sp->x,sp->y,dist,a,b,c);
|
|
|
|
// take off the box around the player or else you'll never get
|
|
// the max_damage;
|
|
dist -= ((int)sp->clipdist)<<(2);
|
|
if (dist < 0) dist = 0;
|
|
|
|
if ((unsigned)dist < wu->Radius)
|
|
{
|
|
damage_per_pixel = IntToFixed(max_damage)/wu->Radius;
|
|
|
|
//the closer your distance is to 0 the more damage
|
|
damage_force = (wu->Radius - dist);
|
|
// fudge factor
|
|
//damage_force += 400;
|
|
|
|
damage_amt = -FixedToInt(damage_force * damage_per_pixel);
|
|
}
|
|
else
|
|
{
|
|
damage_amt = 0;
|
|
}
|
|
|
|
|
|
return damage_amt;
|
|
}
|
|
|
|
|
|
int
|
|
PlayerCheckDeath(PLAYERp pp, short Weapon)
|
|
{
|
|
SPRITEp sp = pp->SpriteP;
|
|
USERp u = User[pp->PlayerSprite];
|
|
int SpawnZombie(PLAYERp pp, short);
|
|
|
|
|
|
// Store off what player was struck by
|
|
pp->HitBy = Weapon;
|
|
|
|
if (u->Health <= 0 && !TEST(pp->Flags, PF_DEAD))
|
|
{
|
|
|
|
// pick a death type
|
|
if (u->LastDamage >= PLAYER_DEATH_EXPLODE_DAMMAGE_AMT)
|
|
pp->DeathType = PLAYER_DEATH_EXPLODE;
|
|
else if (u->LastDamage >= PLAYER_DEATH_CRUMBLE_DAMMAGE_AMT)
|
|
pp->DeathType = PLAYER_DEATH_CRUMBLE;
|
|
else
|
|
pp->DeathType = PLAYER_DEATH_FLIP;
|
|
|
|
if (Weapon < 0)
|
|
{
|
|
pp->Killer = -1;
|
|
DoPlayerBeginDie(pp);
|
|
return TRUE;
|
|
}
|
|
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
if (Weapon > -1 && (wu->ID == RIPPER_RUN_R0 || wu->ID == RIPPER2_RUN_R0))
|
|
pp->DeathType = PLAYER_DEATH_RIPPER;
|
|
|
|
if (Weapon > -1 && wu->ID == CALTROPS)
|
|
pp->DeathType = PLAYER_DEATH_FLIP;
|
|
|
|
if (Weapon > -1 && wu->ID == NINJA_RUN_R0 && wu->PlayerP)
|
|
{
|
|
pp->DeathType = PLAYER_DEATH_FLIP;
|
|
wu->PlayerP->Bloody = TRUE;
|
|
}
|
|
|
|
// keep track of who killed you for death purposes
|
|
// need to check all Killer variables when an enemy dies
|
|
if (pp->Killer < 0)
|
|
{
|
|
if (wp->owner >= 0)
|
|
pp->Killer = wp->owner;
|
|
else if (TEST(wp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
pp->Killer = Weapon;
|
|
}
|
|
|
|
// start the death process
|
|
DoPlayerBeginDie(pp);
|
|
|
|
// for death direction
|
|
//u->slide_ang = wp->ang;
|
|
u->slide_ang = getangle(sp->x - wp->x, sp->y - wp->y);
|
|
// for death velocity
|
|
u->slide_vel = u->LastDamage * 5;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SWBOOL
|
|
PlayerTakeDamage(PLAYERp pp, short Weapon)
|
|
{
|
|
if (Weapon < 0)
|
|
return TRUE;
|
|
|
|
USERp u = User[pp->PlayerSprite];
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
if (gNet.MultiGameType == MULTI_GAME_NONE)
|
|
{
|
|
// ZOMBIE special case for single play
|
|
if (wu->ID == ZOMBIE_RUN_R0)
|
|
{
|
|
// if weapons owner the player
|
|
if (wp->owner == pp->PlayerSprite)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
|
|
{
|
|
// everything hurts you
|
|
if (gNet.HurtTeammate)
|
|
return TRUE;
|
|
|
|
// if weapon IS the YOURSELF take damage
|
|
if (wu->PlayerP == pp)
|
|
return TRUE;
|
|
|
|
// if the weapons owner is YOURSELF take damage
|
|
if (wp->owner >= 0 && User[wp->owner] && User[wp->owner]->PlayerP && User[wp->owner]->PlayerP == pp)
|
|
return TRUE;
|
|
|
|
// if weapon IS the player no damage
|
|
if (wu->PlayerP)
|
|
return FALSE;
|
|
|
|
// if the weapons owner is a player
|
|
if (wp->owner >= 0 && User[wp->owner] && User[wp->owner]->PlayerP)
|
|
return FALSE;
|
|
}
|
|
else if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.TeamPlay)
|
|
{
|
|
// everything hurts you
|
|
if (gNet.HurtTeammate)
|
|
return TRUE;
|
|
|
|
// if weapon IS the YOURSELF take damage
|
|
if (wu->PlayerP == pp)
|
|
return TRUE;
|
|
|
|
// if the weapons owner is YOURSELF take damage
|
|
if (wp->owner >= 0 && User[wp->owner] && User[wp->owner]->PlayerP && User[wp->owner]->PlayerP == pp)
|
|
return TRUE;
|
|
|
|
if (wu->PlayerP)
|
|
{
|
|
// if both on the same team then no damage
|
|
if (wu->spal == u->spal)
|
|
return FALSE;
|
|
}
|
|
|
|
// if the weapons owner is a player
|
|
if (wp->owner >= 0 && User[wp->owner] && User[wp->owner]->PlayerP)
|
|
{
|
|
// if both on the same team then no damage
|
|
if (User[wp->owner]->spal == u->spal)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
StarBlood(short SpriteNum, short Weapon)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
short blood_num = 1;
|
|
short i;
|
|
|
|
if (u->Health <= 0)
|
|
blood_num = 4;
|
|
|
|
for (i = 0; i < blood_num; i++)
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
!AIC KEY - This is where damage is assesed when missiles hit actors and other
|
|
objects.
|
|
|
|
*/
|
|
|
|
int
|
|
DoDamage(short SpriteNum, short Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
int damage=0;
|
|
|
|
ASSERT(u);
|
|
|
|
// don't hit a dead player
|
|
if (u->PlayerP && TEST(u->PlayerP->Flags, PF_DEAD))
|
|
{
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return 0;
|
|
|
|
wp = &sprite[Weapon];
|
|
wu = User[Weapon];
|
|
|
|
ASSERT(wu);
|
|
|
|
if (TEST(wu->Flags, SPR_SUICIDE))
|
|
return 0;
|
|
|
|
ASSERT(u);
|
|
if (u->Attrib && RANDOM_P2(1024) > 850)
|
|
PlaySpriteSound(SpriteNum,attr_pain,v3df_follow);
|
|
|
|
if (TEST(u->Flags, SPR_DEAD))
|
|
{
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// special case for shooting mines
|
|
if (sp->statnum == STAT_MINE_STUCK)
|
|
{
|
|
SpawnMineExp(SpriteNum);
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
// weapon is drivable object manned by player
|
|
if (wu->PlayerP && wu->PlayerP->sop)
|
|
{
|
|
switch (wu->PlayerP->sop->track)
|
|
{
|
|
case SO_TANK:
|
|
damage = -200;
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PLAYERp pp = Player + screenpeek;
|
|
|
|
ActorHealth(SpriteNum, damage);
|
|
if (u->Health <= 0)
|
|
{
|
|
int choosesnd=0;
|
|
// Random chance of taunting the AI's here
|
|
if (STD_RANDOM_RANGE(1024) > 512 && pp == Player+myconnectindex)
|
|
{
|
|
choosesnd=RANDOM_RANGE(MAX_TAUNTAI);
|
|
PlayerSound(TauntAIVocs[choosesnd],v3df_dontpan|v3df_follow,pp);
|
|
}
|
|
SpawnShrap(SpriteNum, Weapon);
|
|
SetSuicide(SpriteNum);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SO_SPEED_BOAT:
|
|
damage = -100;
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Player[0].PlayerSprite == Weapon)
|
|
{
|
|
//DSPRINTF(ds,"got here 4, %d", User[Player[0].PlayerSprite]->ID);
|
|
MONO_PRINT(ds);
|
|
}
|
|
|
|
|
|
// weapon is the actor - no missile used - example swords, axes, etc
|
|
switch (wu->ID)
|
|
{
|
|
case NINJA_RUN_R0:
|
|
|
|
ASSERT(wu->PlayerP);
|
|
|
|
if (wu->WeaponNum == WPN_SWORD)
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_SWORD);
|
|
else
|
|
{
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_FIST);
|
|
// Add damage for stronger attacks!
|
|
switch (wu->PlayerP->WpnKungFuMove)
|
|
{
|
|
case 1:
|
|
damage -= 2 - RANDOM_RANGE(7);
|
|
break;
|
|
case 2:
|
|
damage -= 5 - RANDOM_RANGE(10);
|
|
break;
|
|
}
|
|
PlaySound(DIGI_CGTHIGHBONE,wp,v3df_follow|v3df_dontpan);
|
|
}
|
|
|
|
////DSPRINTF(ds,"got here 3, %d",damage);
|
|
//MONO_PRINT(ds);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case SKEL_RUN_R0:
|
|
case COOLG_RUN_R0:
|
|
case GORO_RUN_R0:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_SKEL_SLASH);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case HORNET_RUN_R0:
|
|
PlaySound(DIGI_HORNETSTING, sp, v3df_follow|v3df_dontpan);
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_HORNET_STING);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
//SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case EEL_RUN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_RIPPER_SLASH);
|
|
damage /= 3;
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case RIPPER_RUN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_RIPPER_SLASH);
|
|
damage /= 3; // Little rippers aren't as tough.
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
if (PlayerCheckDeath(u->PlayerP, Weapon))
|
|
{
|
|
PlaySound(DIGI_RIPPERHEARTOUT,u->PlayerP,v3df_dontpan|v3df_doppler);
|
|
|
|
DoRipperRipHeart(Weapon);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case RIPPER2_RUN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_RIPPER_SLASH);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
if (PlayerCheckDeath(u->PlayerP, Weapon))
|
|
{
|
|
PlaySound(DIGI_RIPPERHEARTOUT,u->PlayerP,v3df_dontpan|v3df_doppler);
|
|
|
|
DoRipper2RipHeart(Weapon);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case BUNNY_RUN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_RIPPER_SLASH);
|
|
damage /= 3;
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
extern int DoBunnyRipHeart(short SpriteNum);
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
if (PlayerCheckDeath(u->PlayerP, Weapon))
|
|
{
|
|
DoBunnyRipHeart(Weapon);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case SERP_RUN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_SERP_SLASH);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage/4, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case BLADE1:
|
|
case BLADE2:
|
|
case BLADE3:
|
|
case 5011:
|
|
|
|
if (wu->ID == 5011)
|
|
damage = -(3 + (RANDOM_RANGE(4<<8)>>8));
|
|
else
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_BLADE);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
if ((u->BladeDamageTics -= synctics) < 0)
|
|
{
|
|
u->BladeDamageTics = DAMAGE_BLADE_TIME;
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((u->BladeDamageTics -= ACTORMOVETICS) < 0)
|
|
{
|
|
u->BladeDamageTics = DAMAGE_BLADE_TIME;
|
|
ActorHealth(SpriteNum, damage);
|
|
}
|
|
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
case STAR1:
|
|
case CROSSBOLT:
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_STAR);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
MONO_PRINT("Stat Hit Actor");
|
|
// Is the player blocking?
|
|
if (u->PlayerP->WpnKungFuMove == 3)
|
|
damage /= 3;
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
MONO_PRINT("Star Hit Actor");
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
StarBlood(SpriteNum, Weapon);
|
|
|
|
wu->ID = 0;
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case SPEAR_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_SPEAR_TRAP);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
wu->ID = 0;
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case LAVA_BOULDER:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_LAVA_BOULDER);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
wu->ID = 0;
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case LAVA_SHARD:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_LAVA_SHARD);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
|
|
wu->ID = 0;
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case UZI_SMOKE:
|
|
case UZI_SMOKE+2:
|
|
if (wu->ID == UZI_SMOKE)
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_UZI);
|
|
else
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_UZI)/3; // Enemy Uzi, 1/3 damage
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
//PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
PlayerDamageSlide(u->PlayerP, damage/2, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
switch (u->ID)
|
|
{
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
case ZILLA_RUN_R0:
|
|
break;
|
|
default:
|
|
if (RANDOM_RANGE(1000) > 900)
|
|
InitBloodSpray(SpriteNum,FALSE,105);
|
|
if (RANDOM_RANGE(1000) > 900)
|
|
SpawnMidSplash(SpriteNum);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
// reset id so no more damage is taken
|
|
wu->ID = 0;
|
|
break;
|
|
|
|
case SHOTGUN_SMOKE:
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_SHOTGUN);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
//SpawnBlood(SpriteNum, Weapon, 0, 0, 0, 0);
|
|
switch (u->ID)
|
|
{
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
case ZILLA_RUN_R0:
|
|
break;
|
|
default:
|
|
if (RANDOM_RANGE(1000) > 950)
|
|
SpawnMidSplash(SpriteNum);
|
|
break;
|
|
}
|
|
|
|
// reset id so no more damage is taken
|
|
wu->ID = 0;
|
|
break;
|
|
|
|
case MIRV_METEOR:
|
|
|
|
//damage = -DAMAGE_MIRV_METEOR;
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_MIRV_METEOR);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
}
|
|
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case SERP_METEOR:
|
|
|
|
//damage = -DAMAGE_SERP_METEOR;
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_SERP_METEOR);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
}
|
|
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case BOLT_THINMAN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_ROCKET);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
}
|
|
|
|
if (wu->Radius == NUKE_RADIUS)
|
|
SpawnNuclearExp(Weapon);
|
|
else
|
|
SpawnBoltExp(Weapon);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case BOLT_THINMAN_R1:
|
|
//damage = -(2000 + (65 + RANDOM_RANGE(40))); // -2000 makes armor not count
|
|
damage = -(65 + RANDOM_RANGE(40));
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
// this is special code to prevent the Zombie from taking out the Bosses to quick
|
|
// if rail gun weapon owner is not player
|
|
if (wp->owner >= 0 && User[wp->owner] && !User[wp->owner]->PlayerP)
|
|
{
|
|
// if actor is a boss
|
|
if (u->ID == ZILLA_RUN_R0 || u->ID == SERP_RUN_R0 || u->ID == SUMO_RUN_R0)
|
|
damage /= 2;
|
|
}
|
|
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage>>1, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
wu->ID = 0; // No more damage
|
|
SpawnTracerExp(Weapon);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case BOLT_THINMAN_R2:
|
|
damage = (GetDamage(SpriteNum, Weapon, WPN_ROCKET)/2);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
}
|
|
|
|
if (wu->Radius == NUKE_RADIUS)
|
|
SpawnNuclearExp(Weapon);
|
|
else
|
|
SpawnBoltExp(Weapon);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case BOLT_THINMAN_R4:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_GRENADE_EXP);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnBunnyExp(Weapon);
|
|
//InitBloodSpray(Weapon,TRUE,-1);
|
|
//InitBloodSpray(Weapon,TRUE,-1);
|
|
//InitBloodSpray(Weapon,TRUE,-1);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case SUMO_RUN_R0:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_FLASHBOMB);
|
|
|
|
damage /= 3;
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
break;
|
|
|
|
case BOLT_EXP:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_BOLT_EXP);
|
|
// //DSPRINTF(ds,"Damage Bolt: %d\n",damage);
|
|
// MONO_PRINT(ds);
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
break;
|
|
|
|
case BLOOD_WORM:
|
|
|
|
// Don't hurt blood worm zombies!
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
break;
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_HEART);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
//PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
if (PlayerCheckDeath(u->PlayerP, Weapon))
|
|
{
|
|
// degrade blood worm life
|
|
wu->Counter3 += (4*120)/MISSILEMOVETICS;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
// degrade blood worm life
|
|
wu->Counter3 += (2*120)/MISSILEMOVETICS;
|
|
|
|
break;
|
|
|
|
|
|
case TANK_SHELL_EXP:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_TANK_SHELL_EXP);
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
break;
|
|
|
|
case MUSHROOM_CLOUD:
|
|
case GRENADE_EXP:
|
|
if (wu->Radius == NUKE_RADIUS) // Special Nuke stuff
|
|
damage = (GetDamage(SpriteNum, Weapon, DMG_NUCLEAR_EXP));
|
|
else
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_GRENADE_EXP);
|
|
|
|
//DSPRINTF(ds,"MUSHROOM: damage = %d, wu->radius = %d\n",damage,wu->Radius);
|
|
MONO_PRINT(ds);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't let it hurt the SUMO
|
|
if (wp->owner >=0 && User[wp->owner] && User[wp->owner]->ID == SUMO_RUN_R0) break; // JBF: added sanity check for wp->owner and User[wp->owner]
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
break;
|
|
|
|
case MICRO_EXP:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_MINE_EXP);
|
|
|
|
// //DSPRINTF(ds,"Damage Micro: %d\n",damage);
|
|
// MONO_PRINT(ds);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
break;
|
|
|
|
case MINE_EXP:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_MINE_EXP);
|
|
if (wp->owner >= 0 && User[wp->owner] && User[wp->owner]->ID == SERP_RUN_R0)
|
|
{
|
|
damage /= 6;
|
|
}
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wp->owner >= 0)
|
|
{
|
|
// Don't let serp skulls hurt the Serpent God
|
|
if (User[wp->owner]->ID == SERP_RUN_R0) break;
|
|
// Don't let it hurt the SUMO
|
|
if (User[wp->owner]->ID == SUMO_RUN_R0) break;
|
|
}
|
|
if (u->ID == TRASHCAN)
|
|
ActorHealth(SpriteNum, -500);
|
|
else
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
// reset id so no more damage is taken
|
|
wu->ID = 0;
|
|
break;
|
|
|
|
#if 0
|
|
case MINE_SHRAP:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_MINE_SHRAP);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
// reset id so no more damage is taken
|
|
wu->ID = 0;
|
|
break;
|
|
#endif
|
|
|
|
case NAP_EXP:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_NAPALM_EXP);
|
|
|
|
// Sumo Nap does less
|
|
if (wp->owner >= 0 && User[wp->owner] && User[wp->owner]->ID == SUMO_RUN_R0)
|
|
damage /= 4;
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't let it hurt the SUMO
|
|
if (User[wp->owner]->ID == SUMO_RUN_R0) break;
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case Vomit1:
|
|
case Vomit2:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_VOMIT);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case COOLG_FIRE:
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_COOLG_FIRE);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
// u->ID = 0;
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
// Skull Exp
|
|
case SKULL_R0:
|
|
case BETTY_R0:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_SKULL_EXP);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
if (TEST(u->sop_parent->flags, SOBJ_DIE_HARD))
|
|
break;
|
|
SopDamage(u->sop_parent, damage);
|
|
SopCheckKill(u->sop_parent);
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, ANG2PLAYER(u->PlayerP, wp));
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorDamageSlide(SpriteNum, damage, ANG2SPRITE(sp, wp));
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
break;
|
|
|
|
// Serp ring of skull
|
|
case SKULL_SERP:
|
|
//DoSkullBeginDeath(Weapon);
|
|
break;
|
|
|
|
case FIREBALL1:
|
|
ASSERT(SpriteNum >= 0 && Weapon >= 0);
|
|
damage = GetDamage(SpriteNum, Weapon, WPN_HOTHEAD);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
if (wp->owner >= 0) // For SerpGod Ring
|
|
User[wp->owner]->Counter--;
|
|
SpawnFireballFlames(Weapon, SpriteNum);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case FIREBALL:
|
|
case GORO_FIREBALL:
|
|
|
|
ASSERT(SpriteNum >= 0 && Weapon >= 0);
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_GORO_FIREBALL);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerDamageSlide(u->PlayerP, damage, wp->ang);
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
if (u->PlayerP->Armor)
|
|
PlaySound(DIGI_ARMORHIT,u->PlayerP,v3df_dontpan|v3df_follow|v3df_doppler);
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorDamageSlide(SpriteNum, damage, wp->ang);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SpawnGoroFireballExp(Weapon);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
case FIREBALL_FLAMES:
|
|
|
|
damage = -DamageData[DMG_FIREBALL_FLAMES].damage_lo;
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
//SpawnFireballFlames(Weapon, SpriteNum);
|
|
|
|
break;
|
|
|
|
case RADIATION_CLOUD:
|
|
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_RADIATION_CLOUD);
|
|
// //DSPRINTF(ds,"Radiation damage = %d\n",damage);
|
|
// MONO_PRINT(ds);
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
PLAYERp pp = u->PlayerP;
|
|
|
|
PlayerSound(DIGI_GASHURT, v3df_dontpan|v3df_follow|v3df_doppler,pp);
|
|
PlayerUpdateHealth(u->PlayerP, damage-1000);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Don't let it hurt the SUMO
|
|
if (wp->owner >= 0 && User[wp->owner]->ID == SUMO_RUN_R0) break; // JBF: added owner check
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
// u->ID = 0;
|
|
wu->ID = 0;
|
|
break;
|
|
|
|
case PLASMA:
|
|
|
|
//damage = GetDamage(SpriteNum, Weapon, WPN_HEART);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
//PlayerUpdateHealth(u->PlayerP, damage);
|
|
//PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
else
|
|
{
|
|
if (u->ID == SKULL_R0 || u->ID == BETTY_R0)
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
SetSuicide(Weapon);
|
|
break;
|
|
}
|
|
else if (u->ID == RIPPER_RUN_R0)
|
|
{
|
|
DoRipperGrow(SpriteNum);
|
|
break;
|
|
}
|
|
|
|
ActorPainPlasma(SpriteNum);
|
|
}
|
|
|
|
InitPlasmaFountain(wp, sp);
|
|
|
|
SetSuicide(Weapon);
|
|
|
|
break;
|
|
|
|
case CALTROPS:
|
|
|
|
ASSERT(SpriteNum >= 0 && Weapon >= 0);
|
|
damage = GetDamage(SpriteNum, Weapon, DMG_MINE_SHRAP);
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
if (PlayerTakeDamage(u->PlayerP, Weapon))
|
|
{
|
|
if (RANDOM_P2(1024<<4)>>4 < 800)
|
|
PlayerSound(DIGI_STEPONCALTROPS, v3df_follow|v3df_dontpan, u->PlayerP);
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
PlayerCheckDeath(u->PlayerP, Weapon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorPain(SpriteNum);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
ActorChooseDeath(SpriteNum, Weapon);
|
|
}
|
|
|
|
SetSuicide(Weapon);
|
|
break;
|
|
|
|
#if 0
|
|
case PLASMA_FOUNTAIN:
|
|
|
|
//damage = GetDamage(SpriteNum, Weapon, WPN_HEART);
|
|
damage = -300;
|
|
|
|
if (u->sop_parent)
|
|
{
|
|
break;
|
|
}
|
|
else if (u->PlayerP)
|
|
{
|
|
PlayerUpdateHealth(u->PlayerP, damage);
|
|
#if 0
|
|
if (PlayerCheckDeath(u->PlayerP, Weapon))
|
|
{
|
|
// Spawn a couple of rippers
|
|
RipperHatch(Weapon);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ActorHealth(SpriteNum, damage);
|
|
ActorStdMissile(SpriteNum, Weapon);
|
|
#if 0
|
|
if (ActorChooseDeath(SpriteNum, Weapon))
|
|
{
|
|
RipperHatch(Weapon);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
|
|
}
|
|
|
|
// If player take alot of damage, make him yell
|
|
if (u && u->PlayerP)
|
|
{
|
|
if (damage <= -40 && RANDOM_RANGE(1000) > 700)
|
|
PlayerSound(DIGI_SONOFABITCH, v3df_dontpan|v3df_follow, u->PlayerP);
|
|
else if (damage <= -40 && RANDOM_RANGE(1000) > 700)
|
|
PlayerSound(DIGI_PAINFORWEAK, v3df_dontpan|v3df_follow, u->PlayerP);
|
|
else if (damage <= -10)
|
|
PlayerSound(PlayerPainVocs[RANDOM_RANGE(MAX_PAIN)], v3df_dontpan|v3df_follow, u->PlayerP);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 1
|
|
// Select death text based on ID
|
|
const char *DeathString(short SpriteNum)
|
|
{
|
|
USERp ku = User[SpriteNum];
|
|
|
|
switch (ku->ID)
|
|
{
|
|
case NINJA_RUN_R0:
|
|
return " ";
|
|
case ZOMBIE_RUN_R0:
|
|
return GStrings("Zombie");
|
|
case BLOOD_WORM:
|
|
return GStrings("Blood Worm");
|
|
case SKEL_RUN_R0:
|
|
return GStrings("Skeletor Priest");
|
|
case COOLG_RUN_R0:
|
|
return GStrings("Coolie Ghost");
|
|
case GORO_RUN_R0:
|
|
return GStrings("Guardian");
|
|
case HORNET_RUN_R0:
|
|
return GStrings("Hornet");
|
|
case RIPPER_RUN_R0:
|
|
return GStrings("Ripper Hatchling");
|
|
case RIPPER2_RUN_R0:
|
|
return GStrings("Ripper");
|
|
case BUNNY_RUN_R0:
|
|
return GStrings("Killer Rabbit");
|
|
case SERP_RUN_R0:
|
|
return GStrings("Serpent god");
|
|
case GIRLNINJA_RUN_R0:
|
|
return GStrings("Girl Ninja");
|
|
case BLADE1:
|
|
case BLADE2:
|
|
case BLADE3:
|
|
case 5011:
|
|
return GStrings("blade");
|
|
case STAR1:
|
|
if (sw_darts) return GStrings("dart");
|
|
else return GStrings("shuriken");
|
|
case CROSSBOLT:
|
|
return GStrings("crossbow bolt");
|
|
case SPEAR_R0:
|
|
return GStrings("spear");
|
|
case LAVA_BOULDER:
|
|
case LAVA_SHARD:
|
|
return GStrings("lava boulder");
|
|
case UZI_SMOKE:
|
|
return GStrings("Uzi");
|
|
case UZI_SMOKE+2:
|
|
return GStrings("Evil Ninja Uzi");
|
|
case SHOTGUN_SMOKE:
|
|
return GStrings("shotgun");
|
|
case MIRV_METEOR:
|
|
case SERP_METEOR:
|
|
return GStrings("meteor");
|
|
case BOLT_THINMAN_R0:
|
|
return GStrings("rocket");
|
|
case BOLT_THINMAN_R1:
|
|
return GStrings("rail gun");
|
|
case BOLT_THINMAN_R2:
|
|
return GStrings("enemy rocket");
|
|
case BOLT_THINMAN_R4: // BunnyRocket
|
|
return GStrings("bunny rocket");
|
|
case BOLT_EXP:
|
|
return GStrings("explosion");
|
|
case TANK_SHELL_EXP:
|
|
return GStrings("tank shell");
|
|
case MUSHROOM_CLOUD:
|
|
return GStrings("nuclear bomb");
|
|
case GRENADE_EXP:
|
|
return GStrings("40mm grenade");
|
|
case MICRO_EXP:
|
|
return GStrings("micro missile");
|
|
case MINE_EXP:
|
|
//case MINE_SHRAP:
|
|
return GStrings("sticky bomb");
|
|
case NAP_EXP:
|
|
return GStrings("napalm");
|
|
case Vomit1:
|
|
case Vomit2:
|
|
return GStrings("vomit");
|
|
case COOLG_FIRE:
|
|
return GStrings("Coolie Ghost phlem");
|
|
case SKULL_R0:
|
|
return GStrings("Accursed Head");
|
|
case BETTY_R0:
|
|
return GStrings("Bouncing Betty");
|
|
case SKULL_SERP:
|
|
return GStrings("Serpent god Protector");
|
|
case FIREBALL1:
|
|
case FIREBALL:
|
|
case GORO_FIREBALL:
|
|
case FIREBALL_FLAMES:
|
|
return GStrings("flames");
|
|
case RADIATION_CLOUD:
|
|
return GStrings("radiation");
|
|
case CALTROPS:
|
|
return GStrings("useitem 7");
|
|
}
|
|
return "";
|
|
}
|
|
#endif
|
|
|
|
int
|
|
DoDamageTest(short Weapon)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
USERp u;
|
|
SPRITEp sp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, tx, ty;
|
|
int tmin;
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
u = User[i];
|
|
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
if ((unsigned)dist > wu->Radius + u->Radius)
|
|
continue;
|
|
|
|
if (sp == wp)
|
|
continue;
|
|
|
|
if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
|
|
continue;
|
|
|
|
// !JIM! Put in a cansee so that you don't take damage through walls and such
|
|
// For speed's sake, try limiting check only to radius weapons!
|
|
if (wu->Radius > 200)
|
|
{
|
|
if (!FAFcansee(sp->x,sp->y, SPRITEp_UPPER(sp), sp->sectnum,wp->x,wp->y,wp->z,wp->sectnum))
|
|
continue;
|
|
}
|
|
|
|
if (wp->owner != i && SpriteOverlap(Weapon, i))
|
|
{
|
|
DoDamage(i, Weapon);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int DoHitscanDamage(short Weapon, uint16_t hit_sprite)
|
|
{
|
|
if (hit_sprite >= MAXSPRITES)
|
|
return 0;
|
|
|
|
unsigned stat;
|
|
|
|
// this routine needs some sort of sprite generated from the hitscan
|
|
// such as a smoke or spark sprite - reason is because of DoDamage()
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
if (sprite[hit_sprite].statnum == StatDamageList[stat])
|
|
{
|
|
DoDamage(hit_sprite, Weapon);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoFlamesDamageTest(short Weapon)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
USERp u;
|
|
SPRITEp sp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, tx, ty;
|
|
int tmin;
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
u = User[i];
|
|
|
|
switch (u->ID)
|
|
{
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
continue;
|
|
}
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
|
|
// //DSPRINTF(ds,"radius = %ld, distance = %ld",wu->Radius+u->Radius,dist);
|
|
// MONO_PRINT(ds);
|
|
|
|
if ((unsigned)dist > wu->Radius + u->Radius)
|
|
continue;
|
|
|
|
if (sp == wp)
|
|
continue;
|
|
|
|
if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN))
|
|
continue;
|
|
|
|
if (TEST(wp->cstat, CSTAT_SPRITE_INVISIBLE))
|
|
continue;
|
|
|
|
if (wu->Radius > 200) // Note: No weaps have bigger radius than 200 cept explosion stuff
|
|
{
|
|
if (FAFcansee(sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum,wp->x,wp->y,SPRITEp_MID(wp),wp->sectnum))
|
|
{
|
|
DoDamage(i, Weapon);
|
|
}
|
|
}
|
|
else if (SpriteOverlap(Weapon, i))
|
|
{
|
|
DoDamage(i, Weapon);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
short PrevWall(short wall_num)
|
|
{
|
|
short start_wall,prev_wall;
|
|
|
|
start_wall = wall_num;
|
|
|
|
do
|
|
{
|
|
prev_wall = wall_num;
|
|
wall_num = wall[wall_num].point2;
|
|
}
|
|
while (wall_num != start_wall);
|
|
|
|
return prev_wall;
|
|
}
|
|
|
|
|
|
short StatBreakList[] =
|
|
{
|
|
STAT_DEFAULT,
|
|
STAT_BREAKABLE,
|
|
STAT_NO_STATE,
|
|
STAT_DEAD_ACTOR,
|
|
};
|
|
|
|
void TraverseBreakableWalls(short start_sect, int x, int y, int z, short ang, int radius)
|
|
{
|
|
int WallBreakPosition(short hit_wall, short *sectnum, int *x, int *y, int *z, short *ang);
|
|
int j, k;
|
|
short sectlist[MAXSECTORS]; // !JIM! Frank, 512 was not big enough for $dozer, was asserting out!
|
|
short sectlistplc, sectlistend, sect, startwall, endwall, nextsector;
|
|
int xmid,ymid;
|
|
int dist;
|
|
short break_count;
|
|
|
|
short sectnum,wall_ang;
|
|
int hit_x,hit_y,hit_z;
|
|
|
|
sectlist[0] = start_sect;
|
|
sectlistplc = 0; sectlistend = 1;
|
|
|
|
// limit radius
|
|
if (radius > 2000)
|
|
radius = 2000;
|
|
|
|
break_count = 0;
|
|
while (sectlistplc < sectlistend)
|
|
{
|
|
sect = sectlist[sectlistplc++];
|
|
|
|
ASSERT((uint16_t)sectlistplc < SIZ(sectlist));
|
|
|
|
startwall = sector[sect].wallptr;
|
|
endwall = startwall + sector[sect].wallnum;
|
|
|
|
for (j = startwall; j < endwall - 1; j++)
|
|
{
|
|
// see if this wall should be broken
|
|
if (wall[j].lotag == TAG_WALL_BREAK)
|
|
{
|
|
// find midpoint
|
|
xmid = DIV2(wall[j].x + wall[j+1].x);
|
|
ymid = DIV2(wall[j].y + wall[j+1].y);
|
|
|
|
// don't need to go further if wall is too far out
|
|
|
|
dist = Distance(xmid, ymid, x, y);
|
|
if (dist > radius)
|
|
continue;
|
|
|
|
if (WallBreakPosition(j, §num, &hit_x, &hit_y, &hit_z, &wall_ang))
|
|
{
|
|
if (hit_x != INT32_MAX && sectnum >= 0 && FAFcansee(x, y, z, start_sect, hit_x, hit_y, hit_z, sectnum))
|
|
{
|
|
//HitBreakWall(&wall[j], x, y, z, ang, 0);
|
|
HitBreakWall(&wall[j], INT32_MAX, INT32_MAX, INT32_MAX, ang, 0);
|
|
|
|
break_count++;
|
|
if (break_count > 4)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
nextsector = wall[j].nextsector;
|
|
|
|
if (nextsector < 0)
|
|
continue;
|
|
|
|
// make sure its not on the list
|
|
for (k = sectlistend - 1; k >= 0; k--)
|
|
{
|
|
if (sectlist[k] == nextsector)
|
|
break;
|
|
}
|
|
|
|
// if its not on the list add it to the end
|
|
if (k < 0)
|
|
{
|
|
sectlist[sectlistend++] = nextsector;
|
|
ASSERT((uint16_t)sectlistend < SIZ(sectlist));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
int DoExpDamageTest(short Weapon)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
USERp u;
|
|
SPRITEp sp;
|
|
short i, nexti, stat;
|
|
int dist, tx, ty;
|
|
int tmin;
|
|
int max_stat;
|
|
short break_count;
|
|
|
|
SPRITEp found_sp = NULL;
|
|
int found_dist = 999999;
|
|
int DoWallMoveMatch(short match);
|
|
|
|
// crack sprites
|
|
if (wu->ID != MUSHROOM_CLOUD)
|
|
WeaponExplodeSectorInRange(Weapon);
|
|
|
|
// Just like DoDamageTest() except that it doesn't care about the owner
|
|
|
|
max_stat = SIZ(StatDamageList);
|
|
// don't check for mines if the weapon is a mine
|
|
if (wp->statnum == STAT_MINE_STUCK)
|
|
max_stat--;
|
|
|
|
for (stat = 0; stat < max_stat; stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
u = User[i];
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
|
|
if ((unsigned)dist > wu->Radius + u->Radius)
|
|
continue;
|
|
|
|
if (sp == wp)
|
|
continue;
|
|
|
|
if (StatDamageList[stat] == STAT_SO_SP_CHILD)
|
|
{
|
|
DoDamage(i, Weapon);
|
|
}
|
|
else
|
|
{
|
|
if ((unsigned)FindDistance3D(sp->x - wp->x, sp->y - wp->y, sp->z - wp->z) > wu->Radius + u->Radius)
|
|
continue;
|
|
|
|
// added hitscan block because mines no long clip against actors/players
|
|
if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN))
|
|
continue;
|
|
|
|
// Second parameter MUST have blocking bits set or cansee won't work
|
|
// added second check for FAF water - hitscans were hitting ceiling
|
|
if (!FAFcansee(wp->x, wp->y, wp->z, wp->sectnum, sp->x, sp->y, SPRITEp_UPPER(sp), sp->sectnum) &&
|
|
!FAFcansee(wp->x, wp->y, wp->z, wp->sectnum, sp->x, sp->y, SPRITEp_LOWER(sp), sp->sectnum))
|
|
continue;
|
|
|
|
DoDamage(i, Weapon);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wu->ID == MUSHROOM_CLOUD) return 0; // Central Nuke doesn't break stuff
|
|
// Only secondaries do that
|
|
|
|
TraverseBreakableWalls(wp->sectnum, wp->x, wp->y, wp->z, wp->ang, wu->Radius);
|
|
|
|
break_count = 0;
|
|
max_stat = SIZ(StatBreakList);
|
|
// Breakable stuff
|
|
for (stat = 0; stat < max_stat; stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatBreakList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
u = User[i];
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
if ((unsigned)dist > wu->Radius)
|
|
continue;
|
|
|
|
dist = FindDistance3D(sp->x - wp->x, sp->y - wp->y, SPRITEp_MID(sp) - wp->z);
|
|
if ((unsigned)dist > wu->Radius)
|
|
continue;
|
|
|
|
if (!FAFcansee(sp->x, sp->y, SPRITEp_MID(sp), sp->sectnum, wp->x, wp->y, wp->z, wp->sectnum))
|
|
continue;
|
|
|
|
if (TEST(sp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(i, wu->ID);
|
|
break_count++;
|
|
if (break_count > 6)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wu->ID == BLOOD_WORM)
|
|
return 0;
|
|
|
|
// wall damaging
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_WALL_MOVE], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
if ((unsigned)dist > wu->Radius/4)
|
|
continue;
|
|
|
|
if (TEST_BOOL1(sp))
|
|
continue;
|
|
|
|
if (!CanSeeWallMove(wp, SP_TAG2(sp)))
|
|
continue;
|
|
|
|
if (dist < found_dist)
|
|
{
|
|
found_dist = dist;
|
|
found_sp = sp;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (found_sp)
|
|
{
|
|
if (SP_TAG2(found_sp) == 0)
|
|
{
|
|
// just do one
|
|
DoWallMove(found_sp);
|
|
}
|
|
else
|
|
{
|
|
if (DoWallMoveMatch(SP_TAG2(found_sp)))
|
|
{
|
|
DoSpawnSpotsForDamage(SP_TAG2(found_sp));
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DoMineExpMine(short Weapon)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
|
|
USERp u;
|
|
SPRITEp sp;
|
|
short i, nexti;
|
|
int dist, tx, ty;
|
|
int tmin;
|
|
int zdist;
|
|
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_MINE_STUCK], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
u = User[i];
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
if ((unsigned)dist > wu->Radius + u->Radius)
|
|
continue;
|
|
|
|
if (sp == wp)
|
|
continue;
|
|
|
|
//if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
|
|
// continue;
|
|
if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN))
|
|
continue;
|
|
|
|
// Explosions are spherical, not planes, so let's check that way, well cylindrical at least.
|
|
zdist = abs(sp->z - wp->z)>>4;
|
|
if (SpriteOverlap(Weapon, i) || (unsigned)zdist < wu->Radius + u->Radius)
|
|
{
|
|
DoDamage(i, Weapon);
|
|
// only explode one mine at a time
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoStar(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
USERp su;
|
|
int vel;
|
|
|
|
// if (!TEST(u->Flags, SPR_BOUNCE))
|
|
// DoBlurExtend(Weapon, 0, 2);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
u->motion_blur_num = 0;
|
|
ScaleSpriteVector(Weapon, 54000);
|
|
|
|
vel = ksqrt(SQ(u->xchange) + SQ(u->ychange));
|
|
|
|
if (vel > 100)
|
|
{
|
|
if ((RANDOM_P2(1024 << 4) >> 4) < 128)
|
|
SpawnBubble(Weapon);
|
|
}
|
|
|
|
sp->z += 128 * MISSILEMOVETICS;
|
|
|
|
DoActorZrange(Weapon);
|
|
MissileWaterAdjust(Weapon);
|
|
|
|
if (sp->z > u->loz)
|
|
{
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vel = ksqrt(SQ(u->xchange) + SQ(u->ychange));
|
|
|
|
|
|
if (vel < 800)
|
|
{
|
|
u->Counter += 50;
|
|
u->zchange += u->Counter;
|
|
}
|
|
}
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (u->ret && !TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_PLAX_WALL:
|
|
break;
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall,nw,wall_ang;
|
|
WALLp wph;
|
|
|
|
hit_wall = NORM_WALL(u->ret);
|
|
wph = &wall[hit_wall];
|
|
|
|
#define STAR_STICK_RNUM 400
|
|
#define STAR_BOUNCE_RNUM 600
|
|
|
|
if (wph->lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hit_wall], sp->x, sp->y, sp->z, sp->ang, u->ID);
|
|
u->ret = 0;
|
|
break;
|
|
}
|
|
|
|
// special case with MissileSetPos - don't queue star
|
|
// from this routine
|
|
if (TEST(u->Flags, SPR_SET_POS_DONT_KILL))
|
|
break;
|
|
|
|
// chance of sticking
|
|
if (!TEST(u->Flags, SPR_BOUNCE) && RANDOM_P2(1024) < STAR_STICK_RNUM)
|
|
{
|
|
u->motion_blur_num = 0;
|
|
ChangeState(Weapon, s_StarStuck);
|
|
sp->xrepeat -= 16;
|
|
sp->yrepeat -= 16;
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
//RESET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
//SET(sp->cstat, CSTAT_SPRITE_BLOCK);
|
|
sp->clipdist = 16L >> 2;
|
|
u->ceiling_dist = Z(2);
|
|
u->floor_dist = Z(2);
|
|
// treat this just like a KillSprite but don't kill
|
|
QueueStar(Weapon);
|
|
return 0;
|
|
}
|
|
|
|
// chance of bouncing
|
|
if (RANDOM_P2(1024) < STAR_BOUNCE_RNUM)
|
|
break;
|
|
|
|
nw = wall[hit_wall].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y)+512);
|
|
|
|
WallBounce(Weapon, wall_ang);
|
|
ScaleSpriteVector(Weapon, 36000);
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->motion_blur_num = 0;
|
|
u->ret = 0;
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
SWBOOL did_hit_wall;
|
|
short hit_sect = NORM_SECTOR(u->ret);
|
|
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
if (SectUser[hit_sect] && SectUser[hit_sect]->depth > 0)
|
|
{
|
|
SpawnSplash(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
// hit water - will be taken care of in WeaponMoveHit
|
|
//break;
|
|
}
|
|
}
|
|
|
|
if (u->lo_sp)
|
|
if (u->lo_sp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
break;
|
|
if (u->hi_sp)
|
|
if (u->hi_sp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
break;
|
|
|
|
ScaleSpriteVector(Weapon, 58000);
|
|
|
|
vel = ksqrt(SQ(u->xchange) + SQ(u->ychange));
|
|
|
|
if (vel < 500)
|
|
break; // will be killed below - u->ret != 0
|
|
|
|
// 32000 to 96000
|
|
u->xchange = mulscale16(u->xchange, 64000 + (RANDOM_RANGE(64000) - 32000));
|
|
u->ychange = mulscale16(u->ychange, 64000 + (RANDOM_RANGE(64000) - 32000));
|
|
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
u->zchange = mulscale16(u->zchange, 50000); // floor
|
|
else
|
|
u->zchange = mulscale16(u->zchange, 40000); // ceiling
|
|
|
|
if (SlopeBounce(Weapon, &did_hit_wall))
|
|
{
|
|
if (did_hit_wall)
|
|
{
|
|
// chance of sticking
|
|
if (RANDOM_P2(1024) < STAR_STICK_RNUM)
|
|
break;
|
|
|
|
// chance of bouncing
|
|
if (RANDOM_P2(1024) < STAR_BOUNCE_RNUM)
|
|
break;
|
|
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->motion_blur_num = 0;
|
|
u->ret = 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
// hit a sloped sector < 45 degrees
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->motion_blur_num = 0;
|
|
u->ret = 0;
|
|
}
|
|
|
|
// BREAK HERE - LOOOK !!!!!!!!!!!!!!!!!!!!!!!!
|
|
break; // hit a slope
|
|
}
|
|
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->motion_blur_num = 0;
|
|
u->ret = 0;
|
|
u->zchange = -u->zchange;
|
|
|
|
// 32000 to 96000
|
|
u->xchange = mulscale16(u->xchange, 64000 + (RANDOM_RANGE(64000) - 32000));
|
|
u->ychange = mulscale16(u->ychange, 64000 + (RANDOM_RANGE(64000) - 32000));
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
u->zchange = mulscale16(u->zchange, 50000); // floor
|
|
else
|
|
u->zchange = mulscale16(u->zchange, 40000); // ceiling
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->ret)
|
|
{
|
|
short hit_sprite = NORM_SPRITE(u->ret);
|
|
if (hit_sprite != -1)
|
|
{
|
|
su = User[hit_sprite];
|
|
if (su && (su->ID == TRASHCAN || su->ID == ZILLA_RUN_R0)) // JBF: added null test
|
|
PlaySound(DIGI_STARCLINK, sp, v3df_none);
|
|
}
|
|
|
|
if (TEST(u->ret, HIT_MASK) != HIT_SPRITE) // Don't clank on sprites
|
|
PlaySound(DIGI_STARCLINK, sp, v3df_none);
|
|
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
int
|
|
DoCrossBolt(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
u = User[Weapon];
|
|
|
|
ASSERT(Weapon >= 0);
|
|
|
|
DoBlurExtend(Weapon, 0, 2);
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, Z(16), Z(16), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
KillSprite(Weapon);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
int
|
|
DoPlasmaDone(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
sp->xrepeat += u->Counter;
|
|
sp->yrepeat -= 4;
|
|
u->Counter += 2;
|
|
|
|
if (sp->yrepeat < 6)
|
|
{
|
|
KillSprite(Weapon);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int PickEnemyTarget(SPRITEp sp, short aware_range)
|
|
{
|
|
TARGET_SORTp ts;
|
|
|
|
DoPickTarget(sp, aware_range, FALSE);
|
|
|
|
for (ts = TargetSort; ts < &TargetSort[TargetSortCount]; ts++)
|
|
{
|
|
if (ts->sprite_num >= 0)
|
|
{
|
|
if (ts->sprite_num == sp->owner || sprite[ts->sprite_num].owner == sp->owner)
|
|
continue;
|
|
|
|
return ts->sprite_num;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
MissileSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range/*, int16_t dang_shift, int16_t turn_limit, int16_t z_limit*/)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
int zh;
|
|
short ang2tgt, delta_ang;
|
|
|
|
SPRITEp hp;
|
|
|
|
if (u->WaitTics <= delay_tics)
|
|
u->WaitTics += MISSILEMOVETICS;
|
|
|
|
if (u->WpnGoal == -1)
|
|
{
|
|
if (u->WaitTics > delay_tics)
|
|
{
|
|
short hit_sprite;
|
|
|
|
if (TEST(u->Flags2, SPR2_DONT_TARGET_OWNER))
|
|
{
|
|
if ((hit_sprite = PickEnemyTarget(sp, aware_range)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
}
|
|
else if ((hit_sprite = DoPickTarget(sp, aware_range, FALSE)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->WpnGoal >= 0)
|
|
{
|
|
hp = &sprite[User[Weapon]->WpnGoal];
|
|
|
|
// move to correct angle
|
|
ang2tgt = getangle(hp->x - sp->x, hp->y - sp->y);
|
|
|
|
delta_ang = GetDeltaAngle(sp->ang, ang2tgt);
|
|
|
|
if (labs(delta_ang) > 32)
|
|
{
|
|
if (delta_ang > 0)
|
|
delta_ang = 32;
|
|
else
|
|
delta_ang = -32;
|
|
}
|
|
|
|
sp->ang -= delta_ang;
|
|
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
|
|
delta_ang = (zh - sp->z)>>1;
|
|
|
|
if (labs(delta_ang) > Z(16))
|
|
{
|
|
if (delta_ang > 0)
|
|
delta_ang = Z(16);
|
|
else
|
|
delta_ang = -Z(16);
|
|
}
|
|
|
|
sp->zvel = delta_ang;
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
u->zchange = sp->zvel;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// combination of vector manipulation
|
|
int
|
|
ComboMissileSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range/*, int16_t dang_shift, int16_t turn_limit, int16_t z_limit*/)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
int dist;
|
|
int zh;
|
|
short ang2tgt, delta_ang;
|
|
|
|
SPRITEp hp;
|
|
|
|
if (u->WaitTics <= delay_tics)
|
|
u->WaitTics += MISSILEMOVETICS;
|
|
|
|
if (u->WpnGoal == -1)
|
|
{
|
|
if (u->WaitTics > delay_tics)
|
|
{
|
|
short hit_sprite;
|
|
|
|
if ((hit_sprite = DoPickTarget(sp, aware_range, FALSE)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->WpnGoal >= 0)
|
|
{
|
|
int oz;
|
|
hp = &sprite[User[Weapon]->WpnGoal];
|
|
|
|
// move to correct angle
|
|
ang2tgt = getangle(hp->x - sp->x, hp->y - sp->y);
|
|
|
|
delta_ang = GetDeltaAngle(sp->ang, ang2tgt);
|
|
|
|
if (labs(delta_ang) > 32)
|
|
{
|
|
if (delta_ang > 0)
|
|
delta_ang = 32;
|
|
else
|
|
delta_ang = -32;
|
|
}
|
|
|
|
sp->ang -= delta_ang;
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
|
|
dist = ksqrt(SQ(sp->x - hp->x) + SQ(sp->y - hp->y) + (SQ(sp->z - zh)>>8));
|
|
|
|
oz = u->zchange;
|
|
|
|
u->zchange = scale(sp->xvel, zh - sp->z, dist);
|
|
u->zchange = (u->zchange + oz*15)/16;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// completely vector manipulation
|
|
int
|
|
VectorMissileSeek(int16_t Weapon, int16_t delay_tics, int16_t turn_speed, int16_t aware_range1, int16_t aware_range2)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
int dist;
|
|
int zh;
|
|
|
|
SPRITEp hp;
|
|
|
|
if (u->WaitTics <= delay_tics)
|
|
u->WaitTics += MISSILEMOVETICS;
|
|
|
|
if (u->WpnGoal == -1)
|
|
{
|
|
if (u->WaitTics > delay_tics)
|
|
{
|
|
short hit_sprite;
|
|
|
|
if (TEST(u->Flags2, SPR2_DONT_TARGET_OWNER))
|
|
{
|
|
if ((hit_sprite = PickEnemyTarget(sp, aware_range1)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
else if ((hit_sprite = PickEnemyTarget(sp, aware_range2)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((hit_sprite = DoPickTarget(sp, aware_range1, FALSE)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
else if ((hit_sprite = DoPickTarget(sp, aware_range2, FALSE)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->WpnGoal >= 0)
|
|
{
|
|
int ox,oy,oz;
|
|
hp = &sprite[User[Weapon]->WpnGoal];
|
|
|
|
if (!hp) return 0;
|
|
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
|
|
dist = ksqrt(SQ(sp->x - hp->x) + SQ(sp->y - hp->y) + (SQ(sp->z - zh)>>8));
|
|
|
|
ox = u->xchange;
|
|
oy = u->ychange;
|
|
oz = u->zchange;
|
|
|
|
u->xchange = scale(sp->xvel, hp->x - sp->x, dist);
|
|
u->ychange = scale(sp->xvel, hp->y - sp->y, dist);
|
|
u->zchange = scale(sp->xvel, zh - sp->z, dist);
|
|
|
|
// the large turn_speed is the slower the turn
|
|
|
|
u->xchange = (u->xchange + ox*(turn_speed-1))/turn_speed;
|
|
u->ychange = (u->ychange + oy*(turn_speed-1))/turn_speed;
|
|
u->zchange = (u->zchange + oz*(turn_speed-1))/turn_speed;
|
|
|
|
sp->ang = getangle(u->xchange, u->ychange);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// completely vector manipulation
|
|
int
|
|
VectorWormSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range1, int16_t aware_range2)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
int dist;
|
|
int zh;
|
|
|
|
SPRITEp hp;
|
|
|
|
if (u->WaitTics <= delay_tics)
|
|
u->WaitTics += MISSILEMOVETICS;
|
|
|
|
if (u->WpnGoal == -1)
|
|
{
|
|
if (u->WaitTics > delay_tics)
|
|
{
|
|
short hit_sprite;
|
|
|
|
if ((hit_sprite = DoPickTarget(sp, aware_range1, FALSE)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
else if ((hit_sprite = DoPickTarget(sp, aware_range2, FALSE)) != -1)
|
|
{
|
|
USERp hu = User[hit_sprite];
|
|
|
|
u->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->WpnGoal >= 0)
|
|
{
|
|
int ox,oy,oz;
|
|
hp = &sprite[User[Weapon]->WpnGoal];
|
|
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
|
|
dist = ksqrt(SQ(sp->x - hp->x) + SQ(sp->y - hp->y) + (SQ(sp->z - zh)>>8));
|
|
|
|
ox = u->xchange;
|
|
oy = u->ychange;
|
|
oz = u->zchange;
|
|
|
|
u->xchange = scale(sp->xvel, hp->x - sp->x, dist);
|
|
u->ychange = scale(sp->xvel, hp->y - sp->y, dist);
|
|
u->zchange = scale(sp->xvel, zh - sp->z, dist);
|
|
|
|
u->xchange = (u->xchange + ox*7)/8;
|
|
u->ychange = (u->ychange + oy*7)/8;
|
|
u->zchange = (u->zchange + oz*7)/8;
|
|
|
|
sp->ang = getangle(u->xchange, u->ychange);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoBlurExtend(int16_t Weapon, int16_t interval, int16_t blur_num)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
if (u->motion_blur_num >= blur_num)
|
|
return 0;
|
|
|
|
u->Counter2++;
|
|
if (u->Counter2 > interval)
|
|
u->Counter2 = 0;
|
|
|
|
if (!u->Counter2)
|
|
{
|
|
u->motion_blur_num++;
|
|
if (u->motion_blur_num > blur_num)
|
|
u->motion_blur_num = blur_num;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitPlasmaFountain(SPRITEp wp, SPRITEp sp)
|
|
{
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short SpriteNum;
|
|
|
|
SpriteNum = SpawnSprite(STAT_MISSILE, PLASMA_FOUNTAIN, s_PlasmaFountain, sp->sectnum,
|
|
sp->x, sp->y, SPRITEp_BOS(sp), sp->ang, 0);
|
|
|
|
np = &sprite[SpriteNum];
|
|
nu = User[SpriteNum];
|
|
|
|
np->shade = -40;
|
|
if (wp)
|
|
SetOwner(wp->owner, SpriteNum);
|
|
SetAttach(sp - sprite, SpriteNum);
|
|
np->yrepeat = 0;
|
|
np->clipdist = 8>>2;
|
|
|
|
// start off on a random frame
|
|
//nu->WaitTics = PLASMA_FOUNTAIN_TIME;
|
|
nu->WaitTics = 120+60;
|
|
nu->Radius = 50;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoPlasmaFountain(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
SPRITEp ap;
|
|
USERp u = User[Weapon];
|
|
short bak_cstat;
|
|
|
|
// if no owner then die
|
|
if (u->Attach < 0)
|
|
{
|
|
KillSprite(Weapon);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
ap = &sprite[u->Attach];
|
|
|
|
// move with sprite
|
|
setspritez(Weapon, (vec3_t *)ap);
|
|
sp->ang = ap->ang;
|
|
|
|
u->Counter++;
|
|
if (u->Counter > 3)
|
|
u->Counter = 0;
|
|
|
|
if (!u->Counter)
|
|
{
|
|
SpawnBlood(ap-sprite, Weapon, 0, 0, 0, 0);
|
|
if (RANDOM_RANGE(1000) > 600)
|
|
InitBloodSpray(ap-sprite, FALSE, 105);
|
|
}
|
|
}
|
|
|
|
// kill the fountain
|
|
if ((u->WaitTics-=MISSILEMOVETICS) <= 0)
|
|
{
|
|
u->WaitTics = 0;
|
|
|
|
bak_cstat = sp->cstat;
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
|
|
//DoDamageTest(Weapon); // fountain not doing the damage an more
|
|
sp->cstat = bak_cstat;
|
|
|
|
KillSprite(Weapon);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoPlasma(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int32_t dax, day, daz;
|
|
int ox,oy,oz;
|
|
|
|
ox = sp->x;
|
|
oy = sp->y;
|
|
oz = sp->z;
|
|
|
|
//MissileSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range/*, int16_t dang_shift, int16_t turn_limit, int16_t z_limit*/)
|
|
//MissileSeek(Weapon, 20, 1024/*, 6, 80, 6*/);
|
|
DoBlurExtend(Weapon, 0, 4);
|
|
|
|
dax = MOVEx(sp->xvel, sp->ang);
|
|
day = MOVEy(sp->xvel, sp->ang);
|
|
daz = sp->zvel;
|
|
|
|
u->ret = move_missile(Weapon, dax, day, daz, Z(16), Z(16), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (u->ret)
|
|
{
|
|
// this sprite is supposed to go through players/enemys
|
|
// if hit a player/enemy back up and do it again with blocking reset
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
short hit_sprite = NORM_SPRITE(u->ret);
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
USERp hu = User[hit_sprite];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_BLOCK) && !TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
short hcstat = hsp->cstat;
|
|
|
|
if (hu && hit_sprite != u->WpnGoal)
|
|
{
|
|
sp->x = ox;
|
|
sp->y = oy;
|
|
sp->z = oz;
|
|
|
|
RESET(hsp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
u->ret = move_missile(Weapon, dax, day, daz, Z(16), Z(16), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
hsp->cstat = hcstat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
{
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
u->Counter = 4;
|
|
ChangeState(Weapon, s_PlasmaDone);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
DoCoolgFire(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
// !JIM! //!FRANK WHY???
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
PlaySound(DIGI_CGMAGICHIT, sp, v3df_follow);
|
|
ChangeState(Weapon, s_CoolgFireDone);
|
|
if (sp->owner >= 0 && User[sp->owner] && User[sp->owner]->ID != RIPPER_RUN_R0) // JBF: added range check
|
|
SpawnDemonFist(Weapon); // Just a red magic circle flash
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoEelFire(short Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
//DoDamageTest(Weapon);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void ScaleSpriteVector(short SpriteNum, int scale)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
|
|
u->xchange = mulscale16(u->xchange, scale);
|
|
u->ychange = mulscale16(u->ychange, scale);
|
|
u->zchange = mulscale16(u->zchange, scale);
|
|
}
|
|
|
|
void WallBounce(short SpriteNum, short ang)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
int old_ang;
|
|
//short hit_wall;
|
|
//short nextwall;
|
|
int k,l;
|
|
//short ang;
|
|
int dax, day;
|
|
|
|
//hit_wall = NORM_WALL(u->ret);
|
|
|
|
//nextwall = wall[hit_wall].point2;
|
|
//ang = getangle(wall[nextwall].x - wall[hit_wall].x, wall[nextwall].y - wall[hit_wall].y)+512;
|
|
|
|
u->bounce++;
|
|
|
|
//k = cos(ang) * sin(ang) * 2
|
|
k = mulscale13(sintable[NORM_ANGLE(ang+512)], sintable[ang]);
|
|
//l = cos(ang * 2)
|
|
l = sintable[NORM_ANGLE((ang*2)+512)];
|
|
|
|
dax = -u->xchange;
|
|
day = -u->ychange;
|
|
|
|
u->xchange = dmulscale14(day, k, dax, l);
|
|
u->ychange = dmulscale14(dax, k, -day, l);
|
|
|
|
old_ang = sp->ang;
|
|
sp->ang = getangle(u->xchange, u->ychange);
|
|
|
|
// hack to prevent missile from sticking to a wall
|
|
//
|
|
if (old_ang == sp->ang)
|
|
{
|
|
u->xchange = -u->xchange;
|
|
u->ychange = -u->ychange;
|
|
sp->ang = getangle(u->xchange, u->ychange);
|
|
}
|
|
}
|
|
|
|
|
|
SWBOOL SlopeBounce(short SpriteNum, SWBOOL *hit_wall)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
int k,l;
|
|
int hiz,loz;
|
|
int slope;
|
|
int dax,day,daz;
|
|
short hit_sector;
|
|
short daang;
|
|
|
|
hit_sector = NORM_SECTOR(u->ret);
|
|
|
|
getzsofslope(hit_sector, sp->x, sp->y, &hiz, &loz);
|
|
|
|
// detect the ceiling and the hit_wall
|
|
if (sp->z < DIV2(hiz+loz))
|
|
{
|
|
if (!TEST(sector[hit_sector].ceilingstat, CEILING_STAT_SLOPE))
|
|
slope = 0;
|
|
else
|
|
slope = sector[hit_sector].ceilingheinum;
|
|
}
|
|
else
|
|
{
|
|
if (!TEST(sector[hit_sector].floorstat, FLOOR_STAT_SLOPE))
|
|
slope = 0;
|
|
else
|
|
slope = sector[hit_sector].floorheinum;
|
|
}
|
|
|
|
if (!slope)
|
|
return FALSE;
|
|
|
|
// if greater than a 45 degree angle
|
|
if (labs(slope) > 4096)
|
|
*hit_wall = TRUE;
|
|
else
|
|
*hit_wall = FALSE;
|
|
|
|
// get angle of the first wall of the sector
|
|
k = sector[hit_sector].wallptr;
|
|
l = wall[k].point2;
|
|
daang = getangle(wall[l].x - wall[k].x, wall[l].y - wall[k].y);
|
|
|
|
// k is now the slope of the ceiling or floor
|
|
|
|
// normal vector of the slope
|
|
dax = mulscale14(slope, sintable[(daang)&2047]);
|
|
day = mulscale14(slope, sintable[(daang+1536)&2047]);
|
|
daz = 4096; // 4096 = 45 degrees
|
|
|
|
// reflection code
|
|
k = ((u->xchange*dax) + (u->ychange*day)) + mulscale4(u->zchange, daz);
|
|
l = (dax*dax) + (day*day) + (daz*daz);
|
|
|
|
// make sure divscale doesn't overflow
|
|
if ((klabs(k)>>14) < l)
|
|
{
|
|
k = divscale17(k, l);
|
|
u->xchange -= mulscale16(dax, k);
|
|
u->ychange -= mulscale16(day, k);
|
|
u->zchange -= mulscale12(daz, k);
|
|
|
|
sp->ang = getangle(u->xchange, u->ychange);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
extern STATE s_Phosphorus[];
|
|
|
|
int
|
|
DoGrenade(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
short i;
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
ScaleSpriteVector(Weapon, 50000);
|
|
|
|
u->Counter += 20;
|
|
u->zchange += u->Counter;
|
|
}
|
|
else
|
|
{
|
|
u->Counter += 20;
|
|
u->zchange += u->Counter;
|
|
}
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
////DSPRINTF(ds, "dist %d, u->ret %d", FindDistance3D(u->xchange, u->ychange, u->zchange), u->ret);
|
|
//MONO_PRINT(ds);
|
|
|
|
if (u->ret)
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_PLAX_WALL:
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
case HIT_SPRITE:
|
|
{
|
|
short wall_ang;
|
|
short hit_sprite = -2;
|
|
SPRITEp hsp;
|
|
|
|
PlaySound(DIGI_40MMBNCE, sp, v3df_dontpan);
|
|
|
|
hit_sprite = NORM_SPRITE(u->ret);
|
|
hsp = &sprite[hit_sprite];
|
|
|
|
// special case so grenade can ring gong
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (TEST(SP_TAG8(hsp), BIT(3)))
|
|
DoMatchEverything(NULL, hsp->hitag, -1);
|
|
}
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
wall_ang = NORM_ANGLE(hsp->ang);
|
|
WallBounce(Weapon, wall_ang);
|
|
ScaleSpriteVector(Weapon, 32000);
|
|
}
|
|
else
|
|
{
|
|
if (u->Counter2 == TRUE) // It's a phosphorus grenade!
|
|
{
|
|
for (i=0; i<5; i++)
|
|
{
|
|
sp->ang = NORM_ANGLE(RANDOM_RANGE(2048));
|
|
InitPhosphorus(Weapon);
|
|
}
|
|
}
|
|
SpawnGrenadeExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall,nw,wall_ang;
|
|
WALLp wph;
|
|
|
|
hit_wall = NORM_WALL(u->ret);
|
|
wph = &wall[hit_wall];
|
|
|
|
if (wph->lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(wph, sp->x, sp->y, sp->z, sp->ang, u->ID);
|
|
u->ret = 0;
|
|
break;
|
|
}
|
|
|
|
PlaySound(DIGI_40MMBNCE, sp, v3df_dontpan);
|
|
|
|
nw = wall[hit_wall].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y)+512);
|
|
|
|
//sp->ang = NORM_ANGLE(sp->ang + 1);
|
|
WallBounce(Weapon, wall_ang);
|
|
ScaleSpriteVector(Weapon, 22000);
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
SWBOOL did_hit_wall;
|
|
if (SlopeBounce(Weapon, &did_hit_wall))
|
|
{
|
|
if (did_hit_wall)
|
|
{
|
|
// hit a wall
|
|
ScaleSpriteVector(Weapon, 22000); // 28000
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
}
|
|
else
|
|
{
|
|
// hit a sector
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
// hit a floor
|
|
if (!TEST(u->Flags, SPR_BOUNCE))
|
|
{
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
ScaleSpriteVector(Weapon, 40000); // 18000
|
|
u->ret = 0;
|
|
u->zchange /= 4;
|
|
u->Counter = 0;
|
|
}
|
|
else
|
|
{
|
|
if (u->Counter2 == TRUE) // It's a phosphorus grenade!
|
|
{
|
|
for (i=0; i<5; i++)
|
|
{
|
|
sp->ang = NORM_ANGLE(RANDOM_RANGE(2048));
|
|
InitPhosphorus(Weapon);
|
|
}
|
|
}
|
|
SpawnGrenadeExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hit a ceiling
|
|
ScaleSpriteVector(Weapon, 22000);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hit floor
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(u->Flags, SPR_BOUNCE); // no bouncing underwater
|
|
|
|
if (u->lo_sectp && SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth)
|
|
SET(u->Flags, SPR_BOUNCE); // no bouncing on shallow water
|
|
|
|
if (!TEST(u->Flags, SPR_BOUNCE))
|
|
{
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
u->zchange = -u->zchange;
|
|
ScaleSpriteVector(Weapon, 40000); // 18000
|
|
u->zchange /= 4;
|
|
PlaySound(DIGI_40MMBNCE, sp, v3df_dontpan);
|
|
}
|
|
else
|
|
{
|
|
if (u->Counter2 == TRUE) // It's a phosphorus grenade!
|
|
{
|
|
for (i=0; i<5; i++)
|
|
{
|
|
sp->ang = NORM_ANGLE(RANDOM_RANGE(2048));
|
|
InitPhosphorus(Weapon);
|
|
}
|
|
}
|
|
//WeaponMoveHit(Weapon);
|
|
SpawnGrenadeExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
// hit something above
|
|
{
|
|
u->zchange = -u->zchange;
|
|
ScaleSpriteVector(Weapon, 22000);
|
|
PlaySound(DIGI_40MMBNCE, sp, v3df_dontpan);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->bounce > 10)
|
|
{
|
|
SpawnGrenadeExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
// if you haven't bounced or your going slow do some puffs
|
|
if (!TEST(u->Flags, SPR_BOUNCE|SPR_UNDERWATER))
|
|
{
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, s_Puff, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 100);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
SetOwner(Weapon, New);
|
|
np->shade = -40;
|
|
np->xrepeat = 40;
|
|
np->yrepeat = 40;
|
|
nu->ox = u->ox;
|
|
nu->oy = u->oy;
|
|
nu->oz = u->oz;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->xchange = u->xchange;
|
|
nu->ychange = u->ychange;
|
|
nu->zchange = u->zchange;
|
|
|
|
ScaleSpriteVector(New, 22000);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoVulcanBoulder(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
u->Counter += 40;
|
|
u->zchange += u->Counter;
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
int32_t const vel = ksqrt(SQ(u->xchange) + SQ(u->ychange));
|
|
|
|
if (vel < 30)
|
|
{
|
|
SpawnLittleExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
if (u->ret)
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_PLAX_WALL:
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
case HIT_SPRITE:
|
|
{
|
|
short wall_ang;
|
|
short hit_sprite = -2;
|
|
SPRITEp hsp;
|
|
|
|
// PlaySound(DIGI_DHCLUNK, sp, v3df_dontpan);
|
|
|
|
hit_sprite = NORM_SPRITE(u->ret);
|
|
hsp = &sprite[hit_sprite];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
wall_ang = NORM_ANGLE(hsp->ang);
|
|
WallBounce(Weapon, wall_ang);
|
|
ScaleSpriteVector(Weapon, 40000);
|
|
}
|
|
else
|
|
{
|
|
// hit an actor
|
|
SpawnLittleExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall,nw,wall_ang;
|
|
WALLp wph;
|
|
|
|
hit_wall = NORM_WALL(u->ret);
|
|
wph = &wall[hit_wall];
|
|
|
|
if (wph->lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(wph, sp->x, sp->y, sp->z, sp->ang, u->ID);
|
|
u->ret = 0;
|
|
break;
|
|
}
|
|
|
|
// PlaySound(DIGI_DHCLUNK, sp, v3df_dontpan);
|
|
|
|
nw = wall[hit_wall].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y)+512);
|
|
|
|
WallBounce(Weapon, wall_ang);
|
|
ScaleSpriteVector(Weapon, 40000);
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
SWBOOL did_hit_wall;
|
|
|
|
if (SlopeBounce(Weapon, &did_hit_wall))
|
|
{
|
|
if (did_hit_wall)
|
|
{
|
|
// hit a sloped sector - treated as a wall because of large slope
|
|
ScaleSpriteVector(Weapon, 30000);
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
}
|
|
else
|
|
{
|
|
// hit a sloped sector
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
// hit a floor
|
|
u->xchange = mulscale16(u->xchange, 30000);
|
|
u->ychange = mulscale16(u->ychange, 30000);
|
|
u->zchange = mulscale16(u->zchange, 12000);
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
|
|
// limit to a reasonable bounce value
|
|
if (u->zchange > Z(32))
|
|
u->zchange = Z(32);
|
|
}
|
|
else
|
|
{
|
|
// hit a sloped ceiling
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
ScaleSpriteVector(Weapon, 30000);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hit unsloped floor
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
|
|
u->xchange = mulscale16(u->xchange, 20000);
|
|
u->ychange = mulscale16(u->ychange, 20000);
|
|
u->zchange = mulscale16(u->zchange, 32000);
|
|
|
|
// limit to a reasonable bounce value
|
|
if (u->zchange > Z(24))
|
|
u->zchange = Z(24);
|
|
|
|
u->zchange = -u->zchange;
|
|
|
|
}
|
|
else
|
|
// hit unsloped ceiling
|
|
{
|
|
u->zchange = -u->zchange;
|
|
ScaleSpriteVector(Weapon, 30000);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SWBOOL
|
|
OwnerIsPlayer(short Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon],uo;
|
|
|
|
if (!u || !sp || (unsigned)sp->owner >= (unsigned)MAXSPRITES) return FALSE;
|
|
uo = User[sp->owner];
|
|
if (uo && uo->PlayerP) return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoMineRangeTest(short Weapon, short range)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
|
|
USERp u;
|
|
SPRITEp sp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, tx, ty;
|
|
int tmin;
|
|
SWBOOL ownerisplayer = FALSE;
|
|
|
|
ownerisplayer = OwnerIsPlayer(Weapon);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
u = User[i];
|
|
|
|
// don't detect the owner or the owners bottom half
|
|
//if (wp->owner == i || (u->PlayerP && (wp->owner == u->PlayerP->PlayerSprite)))
|
|
// continue;
|
|
|
|
DISTANCE(sp->x, sp->y, wp->x, wp->y, dist, tx, ty, tmin);
|
|
if (dist > range)
|
|
continue;
|
|
|
|
if (sp == wp)
|
|
continue;
|
|
|
|
if (!TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
|
|
continue;
|
|
|
|
if (!TEST(sp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
if (u->ID == GIRLNINJA_RUN_R0 && !ownerisplayer)
|
|
continue;
|
|
|
|
dist = FindDistance3D(wp->x - sp->x, wp->y - sp->y, wp->z - sp->z);
|
|
if (dist > range)
|
|
continue;
|
|
|
|
if (!FAFcansee(sp->x,sp->y,SPRITEp_UPPER(sp),sp->sectnum,wp->x,wp->y,wp->z,wp->sectnum))
|
|
continue;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
DoMineStuck(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
#define MINE_DETONATE_STATE 99
|
|
|
|
// if no owner then die
|
|
if (u->Attach >= 0)
|
|
{
|
|
SPRITEp ap = &sprite[u->Attach];
|
|
USERp au = User[u->Attach];
|
|
|
|
ASSERT(au);
|
|
|
|
// Is it attached to a dead actor? Blow it up if so.
|
|
if (TEST(au->Flags, SPR_DEAD) && u->Counter2 < MINE_DETONATE_STATE)
|
|
{
|
|
u->Counter2 = MINE_DETONATE_STATE;
|
|
u->WaitTics = SEC(1)/2;
|
|
}
|
|
|
|
setspritez_old(Weapon, ap->x, ap->y, ap->z - u->sz);
|
|
sp->z = ap->z - DIV2(SPRITEp_SIZE_Z(ap));
|
|
}
|
|
|
|
// not activated yet
|
|
if (!TEST(u->Flags, SPR_ACTIVE))
|
|
{
|
|
if ((u->WaitTics -= (MISSILEMOVETICS*2)) > 0)
|
|
return FALSE;
|
|
|
|
// activate it
|
|
//u->WaitTics = 65536;
|
|
u->WaitTics = 32767;
|
|
u->Counter2 = 0;
|
|
SET(u->Flags, SPR_ACTIVE);
|
|
}
|
|
|
|
// limit the number of times DoMineRangeTest is called
|
|
u->Counter++;
|
|
if (u->Counter > 1)
|
|
u->Counter = 0;
|
|
|
|
if (u->Counter2 != MINE_DETONATE_STATE)
|
|
{
|
|
if ((u->Counter2++) > 30)
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->WaitTics = 32767; // Keep reseting tics to make it stay forever
|
|
u->Counter2 = 0;
|
|
}
|
|
}
|
|
|
|
if (!u->Counter)
|
|
{
|
|
// not already in detonate state
|
|
if (u->Counter2 < MINE_DETONATE_STATE)
|
|
{
|
|
// if something came into range - detonate
|
|
if (DoMineRangeTest(Weapon, 3000))
|
|
{
|
|
// move directly to detonate state
|
|
u->Counter2 = MINE_DETONATE_STATE;
|
|
u->WaitTics = SEC(1)/2;
|
|
}
|
|
}
|
|
}
|
|
|
|
u->WaitTics -= (MISSILEMOVETICS * 2);
|
|
|
|
|
|
// start beeping with pauses
|
|
// quick and dirty beep countdown code
|
|
switch (u->Counter2)
|
|
{
|
|
#if 0
|
|
case 0:
|
|
if (u->WaitTics < SEC(45))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (u->WaitTics < SEC(38))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (u->WaitTics < SEC(30))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (u->WaitTics < SEC(20))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (u->WaitTics < SEC(15))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (u->WaitTics < SEC(12))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (u->WaitTics < SEC(10))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (u->WaitTics < SEC(8))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2++;
|
|
}
|
|
break;
|
|
#endif
|
|
case 30:
|
|
if (u->WaitTics < SEC(6))
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
u->Counter2 = MINE_DETONATE_STATE;
|
|
}
|
|
break;
|
|
// case MINE_DETONATE_STATE:
|
|
// if (u->WaitTics < SEC(5))
|
|
// {
|
|
// start frantic beeping
|
|
// PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
// u->Counter2++;
|
|
// }
|
|
// break;
|
|
case MINE_DETONATE_STATE:
|
|
if (u->WaitTics < 0)
|
|
{
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_dontpan);
|
|
SpawnMineExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
SetMineStuck(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
// stuck
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
// not yet active for 1 sec
|
|
RESET(u->Flags, SPR_ACTIVE);
|
|
u->WaitTics = SEC(3);
|
|
//SET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
u->Counter = 0;
|
|
change_sprite_stat(Weapon, STAT_MINE_STUCK);
|
|
ChangeState(Weapon, s_MineStuck);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoMine(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
// decrease velocity
|
|
ScaleSpriteVector(Weapon, 50000);
|
|
|
|
u->Counter += 20;
|
|
u->zchange += u->Counter;
|
|
}
|
|
else
|
|
{
|
|
//u->Counter += 75;
|
|
u->Counter += 40;
|
|
u->zchange += u->Counter;
|
|
}
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
// check to see if you hit a sprite
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_PLAX_WALL:
|
|
KillSprite(Weapon);
|
|
return 0;
|
|
case HIT_SPRITE:
|
|
{
|
|
short hit_sprite = NORM_SPRITE(u->ret);
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
USERp hu = User[hit_sprite];
|
|
|
|
SetMineStuck(Weapon);
|
|
// Set the Z position
|
|
sp->z = hsp->z - DIV2(SPRITEp_SIZE_Z(hsp));
|
|
|
|
// If it's not alive, don't stick it
|
|
if (hu && hu->Health <= 0) return FALSE; // JBF: added null check
|
|
|
|
// check to see if sprite is player or enemy
|
|
if (TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
USERp uo;
|
|
PLAYERp pp;
|
|
|
|
// attach weapon to sprite
|
|
SetAttach(hit_sprite, Weapon);
|
|
u->sz = sprite[hit_sprite].z - sp->z;
|
|
|
|
if (sp->owner >= 0)
|
|
{
|
|
uo = User[sp->owner];
|
|
|
|
if (uo && uo->PlayerP)
|
|
{
|
|
pp = uo->PlayerP;
|
|
|
|
if (RANDOM_RANGE(1000) > 800)
|
|
PlayerSound(DIGI_STICKYGOTU1, v3df_follow|v3df_dontpan,pp);
|
|
else if (RANDOM_RANGE(1000) > 800)
|
|
PlayerSound(DIGI_STICKYGOTU2, v3df_follow|v3df_dontpan,pp);
|
|
else if (RANDOM_RANGE(1000) > 800)
|
|
PlayerSound(DIGI_STICKYGOTU3, v3df_follow|v3df_dontpan,pp);
|
|
else if (RANDOM_RANGE(1000) > 800)
|
|
PlayerSound(DIGI_STICKYGOTU4, v3df_follow|v3df_dontpan,pp);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
SET(u->Flags2, SPR2_ATTACH_WALL);
|
|
}
|
|
else if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR))
|
|
{
|
|
// hit floor
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
SET(u->Flags2, SPR2_ATTACH_FLOOR);
|
|
else
|
|
SET(u->Flags2, SPR2_ATTACH_CEILING);
|
|
}
|
|
else
|
|
{
|
|
SpawnMineExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall = NORM_WALL(u->ret);
|
|
|
|
if (wall[hit_wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hit_wall], sp->x, sp->y, sp->z, sp->ang, u->ID);
|
|
u->ret = 0;
|
|
break;
|
|
}
|
|
|
|
SetMineStuck(Weapon);
|
|
|
|
SET(u->Flags2, SPR2_ATTACH_WALL);
|
|
|
|
if (TEST(wall[hit_wall].extra, WALLFX_SECTOR_OBJECT))
|
|
{
|
|
}
|
|
|
|
if (TEST(wall[hit_wall].extra, WALLFX_DONT_STICK))
|
|
{
|
|
SpawnMineExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
short hit_sect = NORM_SECTOR(u->ret);
|
|
|
|
SetMineStuck(Weapon);
|
|
|
|
// hit floor
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
SET(u->Flags2, SPR2_ATTACH_FLOOR);
|
|
else
|
|
SET(u->Flags2, SPR2_ATTACH_CEILING);
|
|
|
|
|
|
if (TEST(sector[hit_sect].extra, SECTFX_SECTOR_OBJECT))
|
|
{
|
|
SpawnMineExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
u->ret = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoPuff(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
sp->x += u->xchange;
|
|
sp->y += u->ychange;
|
|
sp->z += u->zchange;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoRailPuff(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
sp->xrepeat += 4;
|
|
sp->yrepeat += 4;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoBoltThinMan(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int32_t dax, day, daz;
|
|
|
|
DoBlurExtend(Weapon, 0, 4);
|
|
|
|
dax = MOVEx(sp->xvel, sp->ang);
|
|
day = MOVEy(sp->xvel, sp->ang);
|
|
daz = sp->zvel;
|
|
|
|
u->ret = move_missile(Weapon, dax, day, daz, CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return TRUE;
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnBoltExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoTracer(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
short i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoEMP(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
short i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (RANDOM_RANGE(1000) > 500)
|
|
{
|
|
sp->xrepeat = 52;
|
|
sp->yrepeat = 10;
|
|
}
|
|
else
|
|
{
|
|
sp->xrepeat = 8;
|
|
sp->yrepeat = 38;
|
|
}
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoEMPBurst(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
if (u->Attach >= 0)
|
|
{
|
|
SPRITEp ap = &sprite[u->Attach];
|
|
USERp au = User[u->Attach];
|
|
|
|
ASSERT(au);
|
|
|
|
setspritez_old(Weapon, ap->x, ap->y, ap->z - u->sz);
|
|
sp->ang = NORM_ANGLE(ap->ang+1024);
|
|
}
|
|
|
|
// not activated yet
|
|
if (!TEST(u->Flags, SPR_ACTIVE))
|
|
{
|
|
// activate it
|
|
u->WaitTics = SEC(7);
|
|
SET(u->Flags, SPR_ACTIVE);
|
|
}
|
|
|
|
if (RANDOM_RANGE(1000) > 500)
|
|
{
|
|
sp->xrepeat = 52;
|
|
sp->yrepeat = 10;
|
|
}
|
|
else
|
|
{
|
|
sp->xrepeat = 8;
|
|
sp->yrepeat = 38;
|
|
}
|
|
|
|
if ((RANDOM_P2(1024<<6)>>6) < 700)
|
|
{
|
|
SpawnShrapX(Weapon);
|
|
}
|
|
|
|
u->WaitTics -= (MISSILEMOVETICS * 2);
|
|
|
|
if (u->WaitTics < 0)
|
|
{
|
|
//SpawnMineExp(Weapon);
|
|
// Spawn a big radius burst of sparks here and check for final damage amount
|
|
KillSprite(Weapon);
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoTankShell(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
short i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnTankShellExp(Weapon);
|
|
//SetExpQuake(exp);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoTracerStart(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoLaser(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
short spawn_count = 0;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
while (TRUE)
|
|
{
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnBoltExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
spawn_count++;
|
|
if (spawn_count < 256)
|
|
{
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, s_LaserPuff, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->shade = -40;
|
|
np->xrepeat = 16;
|
|
np->yrepeat = 16;
|
|
np->pal = nu->spal = PALETTE_RED_LIGHTING;
|
|
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->xchange = nu->ychange = nu->zchange = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
DoLaserStart(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnBoltExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoRail(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
short spawn_count = 0;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
while (TRUE)
|
|
{
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon) && u->ret)
|
|
{
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
short hit_sprite;
|
|
hit_sprite = NORM_SPRITE(u->ret);
|
|
|
|
if (TEST(sprite[hit_sprite].extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
short cstat_save = sprite[hit_sprite].cstat;
|
|
|
|
RESET(sprite[hit_sprite].cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE);
|
|
DoRail(Weapon);
|
|
sprite[hit_sprite].cstat = cstat_save;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
SpawnTracerExp(Weapon);
|
|
SpawnShrapX(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SpawnTracerExp(Weapon);
|
|
SpawnShrapX(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
spawn_count++;
|
|
if (spawn_count < 128)
|
|
{
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, &s_RailPuff[0][0], sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 20);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->xvel += (RANDOM_RANGE(140)-RANDOM_RANGE(140));
|
|
np->yvel += (RANDOM_RANGE(140)-RANDOM_RANGE(140));
|
|
np->zvel += (RANDOM_RANGE(140)-RANDOM_RANGE(140));
|
|
|
|
nu->RotNum = 5;
|
|
NewStateGroup(New, sg_RailPuff);
|
|
|
|
np->shade = -40;
|
|
np->xrepeat = 10;
|
|
np->yrepeat = 10;
|
|
nu->ox = u->ox;
|
|
nu->oy = u->oy;
|
|
nu->oz = u->oz;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->xchange = u->xchange;
|
|
nu->ychange = u->ychange;
|
|
nu->zchange = u->zchange;
|
|
|
|
ScaleSpriteVector(New, 1500);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
DoRailStart(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnTracerExp(Weapon);
|
|
SpawnShrapX(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoRocket(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int dist,a,b,c;
|
|
|
|
|
|
if ((u->FlagOwner -= ACTORMOVETICS)<=0 && u->spal == 20)
|
|
{
|
|
DISTANCE(sp->x, sp->y, u->tgt_sp->x, u->tgt_sp->y, dist, a, b, c);
|
|
u->FlagOwner = dist>>6;
|
|
// Special warn sound attached to each seeker spawned
|
|
PlaySound(DIGI_MINEBEEP, sp, v3df_follow);
|
|
}
|
|
|
|
if (TEST(u->Flags, SPR_FIND_PLAYER))
|
|
{
|
|
//MissileSeek(Weapon, 10, 768/*, 3, 48, 6*/);
|
|
VectorMissileSeek(Weapon, 30, 16, 128, 768);
|
|
}
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return TRUE;
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon) && u->ret)
|
|
{
|
|
if (u->ID == BOLT_THINMAN_R4)
|
|
{
|
|
SpawnBunnyExp(Weapon);
|
|
}
|
|
else if (u->Radius == NUKE_RADIUS)
|
|
SpawnNuclearExp(Weapon);
|
|
else
|
|
SpawnBoltExp(Weapon);
|
|
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!u->Counter)
|
|
{
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, s_Puff, sp->sectnum,
|
|
u->ox, u->oy, u->oz, sp->ang, 100);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
SetOwner(Weapon, New);
|
|
np->shade = -40;
|
|
np->xrepeat = 40;
|
|
np->yrepeat = 40;
|
|
nu->ox = u->ox;
|
|
nu->oy = u->oy;
|
|
nu->oz = u->oz;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->xchange = u->xchange;
|
|
nu->ychange = u->ychange;
|
|
nu->zchange = u->zchange;
|
|
|
|
ScaleSpriteVector(New, 20000);
|
|
|
|
//nu->zchange -= Z(8);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoMicroMini(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
short i;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnMicroExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
SpawnExtraMicroMini(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
short w;
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Micro[0][0], sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, sp->xvel);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(sp->owner, w);
|
|
wp->yrepeat = wp->xrepeat = sp->xrepeat;
|
|
//wp->xrepeat = 64;
|
|
wp->shade = sp->shade;
|
|
wp->clipdist = sp->clipdist;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_MicroMini[0]);
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = u->Radius;
|
|
wu->ceiling_dist = u->ceiling_dist;
|
|
wu->floor_dist = u->floor_dist;
|
|
wp->cstat = sp->cstat;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + RANDOM_RANGE(64) - 32);
|
|
wp->zvel = sp->zvel;
|
|
wp->zvel += RANDOM_RANGE(Z(16)) - Z(8);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoMicro(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
short New;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (!u->Counter)
|
|
{
|
|
SPRITEp np;
|
|
USERp nu;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, s_Puff, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 100);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
SetOwner(sp->owner, New);
|
|
np->shade = -40;
|
|
np->xrepeat = 20;
|
|
np->yrepeat = 20;
|
|
nu->ox = u->ox;
|
|
nu->oy = u->oy;
|
|
nu->oz = u->oz;
|
|
np->zvel = sp->zvel;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->xchange = u->xchange;
|
|
nu->ychange = u->ychange;
|
|
nu->zchange = u->zchange;
|
|
|
|
ScaleSpriteVector(New, 20000);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
|
|
// last smoke
|
|
if ((u->WaitTics -= MISSILEMOVETICS) <= 0)
|
|
{
|
|
setspritez(New, (vec3_t *)np);
|
|
NewStateGroup(Weapon, &sg_MicroMini[0]);
|
|
sp->xrepeat = sp->yrepeat = 10;
|
|
RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
SpawnExtraMicroMini(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// hit something
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnMicroExp(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoUziBullet(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int32_t dax, day, daz;
|
|
int sx,sy;
|
|
short i;
|
|
|
|
// call move_sprite twice for each movement
|
|
// otherwize the moves are in too big an increment
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
dax = MOVEx((sp->xvel >> 1), sp->ang);
|
|
day = MOVEy((sp->xvel >> 1), sp->ang);
|
|
daz = sp->zvel >> 1;
|
|
|
|
sx = sp->x;
|
|
sy = sp->y;
|
|
u->ret = move_missile(Weapon, dax, day, daz, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
u->Dist += Distance(sx, sy, sp->x, sp->y);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 128)
|
|
SpawnBubble(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
SPRITEp wp;
|
|
short j;
|
|
|
|
WeaponMoveHit(Weapon);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, sp->sectnum, sp->x, sp->y, sp->z, sp->ang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SMOKE_REPEAT;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT;
|
|
SetOwner(sp->owner, j);
|
|
wp->ang = sp->ang;
|
|
wp->clipdist = 128 >> 2;
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
|
|
if (!TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, wp->sectnum, wp->x, wp->y, wp->z, 0, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SPARK_REPEAT;
|
|
wp->yrepeat = UZI_SPARK_REPEAT;
|
|
//wp->owner = sp->owner;
|
|
SetOwner(sp->owner, j);
|
|
wp->ang = sp->ang;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
}
|
|
|
|
KillSprite(Weapon);
|
|
|
|
return TRUE;
|
|
}
|
|
else if (u->Dist > 8000)
|
|
{
|
|
KillSprite((short) Weapon);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
DoBoltSeeker(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int32_t dax, day, daz;
|
|
|
|
MissileSeek(Weapon, 30, 768/*, 4, 48, 6*/);
|
|
DoBlurExtend(Weapon, 0, 4);
|
|
|
|
dax = MOVEx(sp->xvel, sp->ang);
|
|
day = MOVEy(sp->xvel, sp->ang);
|
|
daz = sp->zvel;
|
|
|
|
u->ret = move_missile(Weapon, dax, day, daz, CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnBoltExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoBoltShrapnel(int16_t Weapon)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoBoltFatMan(int16_t Weapon)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoElectro(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int32_t dax, day, daz;
|
|
|
|
DoBlurExtend(Weapon, 0, 4);
|
|
|
|
// only seek on Electro's after a hit on an actor
|
|
if (u->Counter > 0)
|
|
MissileSeek(Weapon, 30, 512/*, 3, 52, 2*/);
|
|
|
|
dax = MOVEx(sp->xvel, sp->ang);
|
|
day = MOVEy(sp->xvel, sp->ang);
|
|
daz = sp->zvel;
|
|
|
|
u->ret = move_missile(Weapon, dax, day, daz, CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return TRUE;
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
SPRITEp hsp = &sprite[NORM_SPRITE(u->ret)];
|
|
USERp hu = User[NORM_SPRITE(u->ret)];
|
|
|
|
if (!TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY) || hu->ID == SKULL_R0 || hu->ID == BETTY_R0)
|
|
SpawnShrap(Weapon, -1);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
SpawnShrap(Weapon, -1);
|
|
break;
|
|
}
|
|
|
|
//SpawnShrap(Weapon, -1);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoLavaBoulder(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return TRUE;
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnShrap(Weapon, -1);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoSpear(int16_t Weapon)
|
|
{
|
|
USERp u = User[Weapon];
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
//DoDamageTest(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return TRUE;
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
//SpawnShrap(Weapon, -1);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int SpawnCoolieExp(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum], eu;
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
short explosion;
|
|
SPRITEp exp;
|
|
int zh,nx,ny;
|
|
|
|
ASSERT(u);
|
|
|
|
u->Counter = RANDOM_RANGE(120); // This is the wait til birth time!
|
|
|
|
zh = sp->z - SPRITEp_SIZE_Z(sp) + DIV4(SPRITEp_SIZE_Z(sp));
|
|
nx = sp->x + MOVEx(64, sp->ang+1024);
|
|
ny = sp->y + MOVEy(64, sp->ang+1024);
|
|
|
|
PlaySound(DIGI_COOLIEEXPLODE, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, BOLT_EXP, s_BoltExp, sp->sectnum,
|
|
nx, ny, zh, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(SpriteNum, explosion);
|
|
exp->shade = -40;
|
|
exp->pal = eu->spal = u->spal;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
eu->Radius = DamageData[DMG_BOLT_EXP].radius;
|
|
|
|
DoExpDamageTest(explosion);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
|
|
int
|
|
SpawnBasicExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_MEDIUMEXP, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, BASIC_EXP, s_BasicExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->xrepeat = 24;
|
|
exp->yrepeat = 24;
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->pal = eu->spal = u->spal;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_BASIC_EXP].radius;
|
|
|
|
//
|
|
// All this stuff assures that explosions do not go into floors &
|
|
// ceilings
|
|
//
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(15), Z(15));
|
|
DoExpDamageTest(explosion);
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnFireballFlames(int16_t SpriteNum, int16_t enemy)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp ep;
|
|
USERp eu;
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
return -1;
|
|
|
|
if (enemy >= 0)
|
|
{
|
|
ep = &sprite[enemy];
|
|
eu = User[enemy];
|
|
|
|
// test for already burned
|
|
if (TEST(ep->extra, SPRX_BURNABLE) && ep->shade > 40)
|
|
return -1;
|
|
|
|
if (!eu)
|
|
{
|
|
ASSERT(TRUE == FALSE);
|
|
}
|
|
|
|
if (eu->flame >= 0)
|
|
{
|
|
int sizez = SPRITEp_SIZE_Z(ep) + DIV4(SPRITEp_SIZE_Z(ep));
|
|
np = &sprite[eu->flame];
|
|
nu = User[eu->flame];
|
|
|
|
if (TEST(ep->extra, SPRX_BURNABLE))
|
|
return eu->flame;
|
|
|
|
if (nu->Counter >= SPRITEp_SIZE_Z_2_YREPEAT(np, sizez))
|
|
{
|
|
// keep flame only slightly bigger than the enemy itself
|
|
nu->Counter = SPRITEp_SIZE_Z_2_YREPEAT(np, sizez);
|
|
}
|
|
else
|
|
{
|
|
//increase max size
|
|
nu->Counter += SPRITEp_SIZE_Z_2_YREPEAT(np, 8<<8);
|
|
}
|
|
|
|
// Counter is max size
|
|
if (nu->Counter >= 230)
|
|
{
|
|
// this is far too big
|
|
nu->Counter = 230;
|
|
}
|
|
|
|
if (nu->WaitTics < 2*120)
|
|
nu->WaitTics = 2*120; // allow it to grow again
|
|
|
|
return eu->flame;
|
|
}
|
|
else
|
|
{
|
|
if (eu->PlayerP)
|
|
{
|
|
void SpawnOnFire(PLAYERp pp);
|
|
//SpawnOnFire(eu->PlayerP); //Nobody likes the panel fire
|
|
}
|
|
}
|
|
}
|
|
|
|
New = SpawnSprite(STAT_MISSILE, FIREBALL_FLAMES, s_FireballFlames, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->hitag = LUMINOUS; //Always full brightness
|
|
|
|
if (enemy >= 0)
|
|
eu->flame = New;
|
|
|
|
np->xrepeat = 16;
|
|
np->yrepeat = 16;
|
|
if (enemy >= 0)
|
|
{
|
|
// large flame for trees and such
|
|
if (TEST(ep->extra, SPRX_BURNABLE))
|
|
{
|
|
int sizez = SPRITEp_SIZE_Z(ep) + DIV4(SPRITEp_SIZE_Z(ep));
|
|
nu->Counter = SPRITEp_SIZE_Z_2_YREPEAT(np, sizez);
|
|
}
|
|
else
|
|
{
|
|
nu->Counter = SPRITEp_SIZE_Z_2_YREPEAT(np, SPRITEp_SIZE_Z(ep)>>1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nu->Counter = 48; // max flame size
|
|
}
|
|
|
|
SetOwner(sp->owner, New);
|
|
np->shade = -40;
|
|
np->pal = nu->spal = u->spal;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
//nu->Radius = DamageData[DMG_FIREBALL_FLAMES].radius;
|
|
nu->Radius = 200;
|
|
|
|
if (enemy >= 0)
|
|
{
|
|
SetAttach(enemy, New);
|
|
}
|
|
else
|
|
{
|
|
if (TestDontStickSector(np->sectnum))
|
|
{
|
|
KillSprite(New);
|
|
return -1;
|
|
}
|
|
|
|
nu->floor_dist = nu->ceiling_dist = 0;
|
|
|
|
DoFindGround(New);
|
|
nu->jump_speed = 0;
|
|
DoBeginJump(New);
|
|
}
|
|
|
|
PlaySound(DIGI_FIRE1,np,v3df_dontpan|v3df_doppler);
|
|
Set3DSoundOwner(New);
|
|
|
|
return New;
|
|
}
|
|
|
|
|
|
int
|
|
SpawnBreakFlames(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, FIREBALL_FLAMES+1, s_BreakFlames, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->hitag = LUMINOUS; //Always full brightness
|
|
|
|
np->xrepeat = 16;
|
|
np->yrepeat = 16;
|
|
nu->Counter = 48; // max flame size
|
|
|
|
//SetOwner(sp->owner, New);
|
|
np->shade = -40;
|
|
if (u)
|
|
np->pal = nu->spal = u->spal;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->Radius = 200;
|
|
|
|
nu->floor_dist = nu->ceiling_dist = 0;
|
|
|
|
DoFindGround(New);
|
|
nu->jump_speed = 0;
|
|
DoBeginJump(New);
|
|
|
|
PlaySound(DIGI_FIRE1,np,v3df_dontpan|v3df_doppler);
|
|
Set3DSoundOwner(New);
|
|
|
|
return New;
|
|
}
|
|
|
|
|
|
int
|
|
SpawnBreakStaticFlames(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
New = SpawnSprite(STAT_STATIC_FIRE, FIREBALL_FLAMES, NULL, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
if (RANDOM_RANGE(1000) > 500)
|
|
np->picnum = 3143;
|
|
else
|
|
np->picnum = 3157;
|
|
|
|
np->hitag = LUMINOUS; //Always full brightness
|
|
|
|
//np->xrepeat = 64;
|
|
//np->yrepeat = 64;
|
|
np->xrepeat = 32;
|
|
np->yrepeat = 32;
|
|
//nu->Counter = 48; // max flame size
|
|
|
|
//SetOwner(sp->owner, New);
|
|
np->shade = -40;
|
|
np->pal = nu->spal = u->spal;
|
|
//SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
nu->Radius = 200;
|
|
|
|
nu->floor_dist = nu->ceiling_dist = 0;
|
|
|
|
np->z = getflorzofslope(np->sectnum,np->x,np->y);
|
|
|
|
//DoFindGround(New);
|
|
//nu->jump_speed = 0;
|
|
//DoBeginJump(New);
|
|
|
|
PlaySound(DIGI_FIRE1,np,v3df_dontpan|v3df_doppler);
|
|
Set3DSoundOwner(New);
|
|
|
|
return New;
|
|
}
|
|
|
|
|
|
int
|
|
SpawnFireballExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_SMALLEXP, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, FIREBALL_EXP, s_FireballExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->xrepeat = 52;
|
|
exp->yrepeat = 52;
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->pal = eu->spal = u->spal;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(eu->Flags, TEST(u->Flags,SPR_UNDERWATER));
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
//
|
|
// All this stuff assures that explosions do not go into floors &
|
|
// ceilings
|
|
//
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(15), Z(15));
|
|
|
|
if (RANDOM_P2(1024) < 150)
|
|
SpawnFireballFlames(explosion,-1);
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnGoroFireballExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_MEDIUMEXP, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, 0, s_FireballExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->xrepeat = 16;
|
|
exp->yrepeat = 16;
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->pal = eu->spal = u->spal;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
//
|
|
// All this stuff assures that explosions do not go into floors &
|
|
// ceilings
|
|
//
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(15), Z(15));
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnBoltExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_BOLTEXPLODE, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, BOLT_EXP, s_BoltExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 76;
|
|
exp->yrepeat = 76;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
eu->Radius = DamageData[DMG_BOLT_EXP].radius;
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(40), Z(40));
|
|
|
|
DoExpDamageTest(explosion);
|
|
|
|
SetExpQuake(explosion); // !JIM! made rocket launcher shake things
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnBunnyExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
ASSERT(u);
|
|
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_BUNNYDIE3, sp, v3df_none);
|
|
|
|
u->ID = BOLT_EXP; // Change id
|
|
InitBloodSpray(Weapon,TRUE,-1);
|
|
InitBloodSpray(Weapon,TRUE,-1);
|
|
InitBloodSpray(Weapon,TRUE,-1);
|
|
DoExpDamageTest(Weapon);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnTankShellExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_BOLTEXPLODE, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, TANK_SHELL_EXP, s_TankShellExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 64+32;
|
|
exp->yrepeat = 64+32;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
eu->Radius = DamageData[DMG_TANK_SHELL_EXP].radius;
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(40), Z(40));
|
|
DoExpDamageTest(explosion);
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
|
|
int
|
|
SpawnNuclearSecondaryExp(int16_t Weapon, short ang)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, GRENADE_EXP, s_GrenadeExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 512);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -128;
|
|
exp->xrepeat = 218;
|
|
exp->yrepeat = 152;
|
|
exp->clipdist = sp->clipdist;
|
|
eu->ceiling_dist = Z(16);
|
|
eu->floor_dist = Z(16);
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
//ang = RANDOM_P2(2048);
|
|
int32_t const vel = (2048+128) + RANDOM_RANGE(2048);
|
|
eu->xchange = MOVEx(vel, ang);
|
|
eu->ychange = MOVEy(vel, ang);
|
|
eu->Radius = 200; // was NUKE_RADIUS
|
|
eu->ret = move_missile(explosion, eu->xchange, eu->ychange, 0,
|
|
eu->ceiling_dist, eu->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (FindDistance3D(exp->x - sp->x, exp->y - sp->y, exp->z - sp->z) < 1024)
|
|
{
|
|
KillSprite(explosion);
|
|
return -1;
|
|
}
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(50), Z(10));
|
|
|
|
InitChemBomb(explosion);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnNuclearExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion,ang=0;
|
|
PLAYERp pp = NULL;
|
|
short rnd_rng;
|
|
|
|
ASSERT(u);
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_NUCLEAREXP, sp, v3df_dontpan | v3df_doppler);
|
|
|
|
if (sp->owner)
|
|
{
|
|
pp = User[sp->owner]->PlayerP;
|
|
rnd_rng = RANDOM_RANGE(1000);
|
|
|
|
if (rnd_rng > 990)
|
|
PlayerSound(DIGI_LIKEHIROSHIMA, v3df_follow|v3df_dontpan,pp);
|
|
else if (rnd_rng > 980)
|
|
PlayerSound(DIGI_LIKENAGASAKI, v3df_follow|v3df_dontpan,pp);
|
|
else if (rnd_rng > 970)
|
|
PlayerSound(DIGI_LIKEPEARL, v3df_follow|v3df_dontpan,pp);
|
|
}
|
|
|
|
// Spawn big mushroom cloud
|
|
explosion = SpawnSprite(STAT_MISSILE, MUSHROOM_CLOUD, s_NukeMushroom, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -128;
|
|
exp->xrepeat = 255;
|
|
exp->yrepeat = 255;
|
|
exp->clipdist = sp->clipdist;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->spal = exp->pal = PALETTE_PLAYER1; // Set nuke puff to gray
|
|
|
|
InitChemBomb(explosion);
|
|
|
|
|
|
// Do central explosion
|
|
explosion = SpawnSprite(STAT_MISSILE, MUSHROOM_CLOUD, s_GrenadeExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -128;
|
|
exp->xrepeat = 218;
|
|
exp->yrepeat = 152;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
|
|
eu->Radius = NUKE_RADIUS;
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(30), Z(30));
|
|
|
|
DoExpDamageTest(explosion);
|
|
|
|
// Nuclear effects
|
|
SetNuclearQuake(explosion);
|
|
|
|
// if (pp->NightVision)
|
|
// {
|
|
// SetFadeAmt(pp, -300, 1); // Idiot had night vision on in nuke flash
|
|
// PlayerUpdateHealth(pp,-25); // Just burned your eyes out of their sockets!
|
|
// }
|
|
// else
|
|
SetFadeAmt(pp, -80, 1); // Nuclear flash
|
|
|
|
// Secondary blasts
|
|
ang = RANDOM_P2(2048);
|
|
SpawnNuclearSecondaryExp(explosion, ang);
|
|
ang = ang + 512 + RANDOM_P2(256);
|
|
SpawnNuclearSecondaryExp(explosion, ang);
|
|
ang = ang + 512 + RANDOM_P2(256);
|
|
SpawnNuclearSecondaryExp(explosion, ang);
|
|
ang = ang + 512 + RANDOM_P2(256);
|
|
SpawnNuclearSecondaryExp(explosion, ang);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnTracerExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
if (u->ID == BOLT_THINMAN_R1)
|
|
explosion = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R1, s_TracerExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
else
|
|
explosion = SpawnSprite(STAT_MISSILE, TRACER_EXP, s_TracerExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 4;
|
|
exp->yrepeat = 4;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
|
|
if (u->ID == BOLT_THINMAN_R1)
|
|
{
|
|
eu->Radius = DamageData[DMG_BASIC_EXP].radius;
|
|
DoExpDamageTest(explosion);
|
|
}
|
|
else
|
|
eu->Radius = DamageData[DMG_BOLT_EXP].radius;
|
|
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnMicroExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
// PlaySound(DIGI_MISSLEXP, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, MICRO_EXP, s_MicroExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 32;
|
|
exp->yrepeat = 32;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_YFLIP);
|
|
eu->Radius = DamageData[DMG_BOLT_EXP].radius;
|
|
|
|
//
|
|
// All this stuff assures that explosions do not go into floors &
|
|
// ceilings
|
|
//
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(20), Z(20));
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
AddSpriteToSectorObject(short SpriteNum, SECTOR_OBJECTp sop)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
unsigned sn;
|
|
|
|
// make sure it has a user
|
|
if (!u)
|
|
{
|
|
u = SpawnUser(SpriteNum, 0, NULL);
|
|
}
|
|
|
|
// find a free place on this list
|
|
for (sn = 0; sn < SIZ(sop->sp_num); sn++)
|
|
{
|
|
if (sop->sp_num[sn] == -1)
|
|
break;
|
|
}
|
|
|
|
ASSERT(sn < SIZ(sop->sp_num) - 1);
|
|
sop->sp_num[sn] = SpriteNum;
|
|
so_setspriteinterpolation(sop, sp);
|
|
|
|
SET(u->Flags, SPR_ON_SO_SECTOR|SPR_SO_ATTACHED);
|
|
|
|
u->sx = sop->xmid - sp->x;
|
|
u->sy = sop->ymid - sp->y;
|
|
u->sz = sector[sop->mid_sector].floorz - sp->z;
|
|
|
|
u->sang = sp->ang;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnBigGunFlames(int16_t Weapon, int16_t Operator, SECTOR_OBJECTp sop)
|
|
{
|
|
SPRITEp sp;
|
|
USERp u;
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
unsigned sn;
|
|
SWBOOL smallflames = FALSE;
|
|
|
|
if (Weapon < 0)
|
|
{
|
|
Weapon = abs(Weapon);
|
|
smallflames = TRUE;
|
|
}
|
|
|
|
sp = &sprite[Weapon];
|
|
u = User[Weapon];
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, MICRO_EXP, s_BigGunFlame, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(Operator, explosion);
|
|
exp->shade = -40;
|
|
if (smallflames)
|
|
{
|
|
exp->xrepeat = 12;
|
|
exp->yrepeat = 12;
|
|
}
|
|
else
|
|
{
|
|
exp->xrepeat = 34;
|
|
exp->yrepeat = 34;
|
|
}
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
// place all sprites on list
|
|
for (sn = 0; sn < SIZ(sop->sp_num); sn++)
|
|
{
|
|
if (sop->sp_num[sn] == -1)
|
|
break;
|
|
}
|
|
|
|
ASSERT(sn < SIZ(sop->sp_num) - 1);
|
|
sop->sp_num[sn] = explosion;
|
|
so_setspriteinterpolation(sop, exp);
|
|
|
|
// Place sprite exactly where shoot point is
|
|
//exp->x = eu->ox = sop->xmid - u->sx;
|
|
//exp->y = eu->oy = sop->ymid - u->sy;
|
|
|
|
SET(eu->Flags, TEST(u->Flags, SPR_ON_SO_SECTOR|SPR_SO_ATTACHED));
|
|
|
|
if (TEST(u->Flags, SPR_ON_SO_SECTOR))
|
|
{
|
|
// move with sector its on
|
|
exp->z = eu->oz = sector[sp->sectnum].floorz - u->sz;
|
|
}
|
|
else
|
|
{
|
|
// move with the mid sector
|
|
exp->z = eu->oz = sector[sop->mid_sector].floorz - u->sz;
|
|
}
|
|
|
|
eu->sx = u->sx;
|
|
eu->sy = u->sy;
|
|
eu->sz = u->sz;
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnGrenadeSecondaryExp(int16_t Weapon, short ang)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
int vel;
|
|
|
|
|
|
ASSERT(u);
|
|
explosion = SpawnSprite(STAT_MISSILE, GRENADE_EXP, s_GrenadeSmallExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 1024);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 32;
|
|
exp->yrepeat = 32;
|
|
exp->clipdist = sp->clipdist;
|
|
eu->ceiling_dist = Z(16);
|
|
eu->floor_dist = Z(16);
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
//ang = RANDOM_P2(2048);
|
|
vel = (1024+512) + RANDOM_RANGE(1024);
|
|
eu->xchange = MOVEx(vel, ang);
|
|
eu->ychange = MOVEy(vel, ang);
|
|
|
|
eu->ret = move_missile(explosion, eu->xchange, eu->ychange, 0,
|
|
eu->ceiling_dist, eu->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (FindDistance3D(exp->x - sp->x, exp->y - sp->y, exp->z - sp->z) < 1024)
|
|
{
|
|
KillSprite(explosion);
|
|
return -1;
|
|
}
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(50), Z(10));
|
|
|
|
eu->ox = exp->x;
|
|
eu->oy = exp->y;
|
|
eu->oz = exp->z;
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnGrenadeSmallExp(int16_t Weapon)
|
|
{
|
|
short ang;
|
|
|
|
ang = RANDOM_P2(2048);
|
|
SpawnGrenadeSecondaryExp(Weapon, ang);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnGrenadeExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
int dx,dy,dz;
|
|
|
|
ASSERT(u);
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_30MMEXPLODE, sp, v3df_none);
|
|
|
|
if (RANDOM_RANGE(1000) > 990)
|
|
{
|
|
if (sp->owner >= 0 && User[sp->owner] && User[sp->owner]->PlayerP)
|
|
{
|
|
PLAYERp pp = User[sp->owner]->PlayerP;
|
|
PlayerSound(DIGI_LIKEFIREWORKS, v3df_follow|v3df_dontpan,pp);
|
|
}
|
|
}
|
|
|
|
dx = sp->x;
|
|
dy = sp->y;
|
|
dz = sp->z;
|
|
|
|
if (u->ID == ZILLA_RUN_R0)
|
|
{
|
|
dx += RANDOM_RANGE(1000)-RANDOM_RANGE(1000);
|
|
dy += RANDOM_RANGE(1000)-RANDOM_RANGE(1000);
|
|
dz = SPRITEp_MID(sp) + RANDOM_RANGE(1000)-RANDOM_RANGE(1000);
|
|
}
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, GRENADE_EXP, s_GrenadeExp, sp->sectnum,
|
|
dx, dy, dz, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 64 + 32;
|
|
exp->yrepeat = 64 + 32;
|
|
exp->clipdist = sp->clipdist;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_GRENADE_EXP].radius;
|
|
|
|
//
|
|
// All this stuff assures that explosions do not go into floors &
|
|
// ceilings
|
|
//
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(100), Z(30));
|
|
|
|
DoExpDamageTest(explosion);
|
|
//InitMineShrap(explosion);
|
|
|
|
SetExpQuake(explosion);
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 0);
|
|
|
|
#if 0
|
|
short ang;
|
|
ang = RANDOM_P2(2048);
|
|
SpawnGrenadeSecondaryExp(explosion, ang);
|
|
ang = ang + 512 + RANDOM_P2(256);
|
|
SpawnGrenadeSecondaryExp(explosion, ang);
|
|
ang = ang + 512 + RANDOM_P2(256);
|
|
SpawnGrenadeSecondaryExp(explosion, ang);
|
|
ang = ang + 512 + RANDOM_P2(256);
|
|
SpawnGrenadeSecondaryExp(explosion, ang);
|
|
#endif
|
|
|
|
return explosion;
|
|
}
|
|
|
|
void SpawnExpZadjust(short Weapon, SPRITEp exp, int upper_zsize, int lower_zsize)
|
|
{
|
|
USERp u = User[Weapon];
|
|
USERp eu = User[exp - sprite];
|
|
int tos_z, bos_z;
|
|
|
|
ASSERT(eu);
|
|
|
|
if (u)
|
|
{
|
|
tos_z = exp->z - upper_zsize;
|
|
bos_z = exp->z + lower_zsize;
|
|
|
|
if (tos_z <= u->hiz + Z(4))
|
|
{
|
|
exp->z = u->hiz + upper_zsize;
|
|
SET(exp->cstat, CSTAT_SPRITE_YFLIP);
|
|
}
|
|
else if (bos_z > u->loz)
|
|
{
|
|
exp->z = u->loz - lower_zsize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int cz,fz;
|
|
|
|
getzsofslope(exp->sectnum, exp->x, exp->y, &cz, &fz);
|
|
|
|
tos_z = exp->z - upper_zsize;
|
|
bos_z = exp->z + lower_zsize;
|
|
|
|
if (tos_z <= cz + Z(4))
|
|
{
|
|
exp->z = cz + upper_zsize;
|
|
SET(exp->cstat, CSTAT_SPRITE_YFLIP);
|
|
}
|
|
else if (bos_z > fz)
|
|
{
|
|
exp->z = fz - lower_zsize;
|
|
}
|
|
}
|
|
|
|
eu->oz = exp->z;
|
|
}
|
|
int
|
|
SpawnMineExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
if (u && TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
change_sprite_stat(Weapon, STAT_MISSILE);
|
|
|
|
PlaySound(DIGI_MINEBLOW, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, MINE_EXP, s_MineExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->xrepeat = 64 + 44;
|
|
exp->yrepeat = 64 + 44;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_MINE_EXP].radius;
|
|
|
|
//
|
|
// All this stuff assures that explosions do not go into floors &
|
|
// ceilings
|
|
//
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(100), Z(20));
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
SetExpQuake(explosion);
|
|
|
|
//DoExpDamageTest(explosion);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
|
|
int
|
|
InitMineShrap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
SPRITEp wp;
|
|
short ang, w, i;
|
|
hitdata_t hitinfo;
|
|
int daz;
|
|
|
|
for (i = 0; i < 18; i++)
|
|
{
|
|
ang = RANDOM_P2(2048);
|
|
daz = Z(RANDOM_P2(48))<<3;
|
|
daz -= DIV2(Z(48)<<3);
|
|
|
|
FAFhitscan(sp->x, sp->y, sp->z - Z(30), sp->sectnum, // Start position
|
|
sintable[NORM_ANGLE(ang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(ang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
continue;
|
|
|
|
|
|
// check to see what you hit
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
}
|
|
|
|
#define MINE_SHRAP_DIST_MAX 20000
|
|
if (Distance(hitinfo.pos.x, hitinfo.pos.y, sp->x, sp->y) > MINE_SHRAP_DIST_MAX)
|
|
continue;
|
|
|
|
w = SpawnSprite(STAT_MISSILE, MINE_SHRAP, s_MineSpark, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang, 0);
|
|
wp = &sprite[w];
|
|
wp->shade = -40;
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(sp->owner, w);
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
HitscanSpriteAdjust(w, hitinfo.wall);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int DoMineExp(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
DoExpDamageTest(SpriteNum);
|
|
//InitMineShrap(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoSectorExp(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
sp->x += u->xchange;
|
|
sp->y += u->ychange;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnSectorExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
PlaySound(DIGI_30MMEXPLODE, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, GRENADE_EXP, s_SectorExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->owner = -1;
|
|
exp->shade = -40;
|
|
exp->xrepeat = 90; // was 40,40
|
|
exp->yrepeat = 90;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_SECTOR_EXP].radius;
|
|
|
|
DoExpDamageTest(explosion);
|
|
SetExpQuake(explosion);
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
// called from SpawnShrap
|
|
int
|
|
SpawnLargeExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
PlaySound(DIGI_30MMEXPLODE, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, GRENADE_EXP, s_SectorExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->owner = -1;
|
|
exp->shade = -40;
|
|
exp->xrepeat = 90; // was 40,40
|
|
exp->yrepeat = 90;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_SECTOR_EXP].radius;
|
|
|
|
SpawnExpZadjust(Weapon, exp, Z(50), Z(50));
|
|
|
|
// Should not cause other sectors to explode
|
|
DoExpDamageTest(explosion);
|
|
SetExpQuake(explosion);
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnMeteorExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
if (u->spal == 25) // Serp ball
|
|
{
|
|
explosion = SpawnSprite(STAT_MISSILE, METEOR_EXP, s_TeleportEffect2, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
}
|
|
else
|
|
{
|
|
PlaySound(DIGI_MEDIUMEXP, sp, v3df_none);
|
|
explosion = SpawnSprite(STAT_MISSILE, METEOR_EXP, s_MeteorExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
}
|
|
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->owner = -1;
|
|
exp->shade = -40;
|
|
if (sp->yrepeat < 64)
|
|
{
|
|
// small
|
|
exp->xrepeat = 64;
|
|
exp->yrepeat = 64;
|
|
}
|
|
else
|
|
{
|
|
// large - boss
|
|
exp->xrepeat = 80;
|
|
exp->yrepeat = 80;
|
|
}
|
|
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_BASIC_EXP].radius;
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
SpawnLittleExp(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
PlaySound(DIGI_HEADSHOTHIT, sp, v3df_none);
|
|
explosion = SpawnSprite(STAT_MISSILE, BOLT_EXP, s_SectorExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->owner = -1;
|
|
exp->shade = -127;
|
|
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_BASIC_EXP].radius;
|
|
DoExpDamageTest(explosion);
|
|
SpawnVis(-1, exp->sectnum, exp->x, exp->y, exp->z, 16);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
DoFireball(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
u = User[Weapon];
|
|
|
|
ASSERT(Weapon >= 0);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
sp->xrepeat = sp->yrepeat -= 1;
|
|
if (sp->xrepeat <= 37)
|
|
{
|
|
SpawnSmokePuff(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
ox = sp->x;
|
|
oy = sp->y;
|
|
oz = sp->z;
|
|
os = sp->sectnum
|
|
#endif
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
#if 0
|
|
if (u->ret)
|
|
{
|
|
u->ret = 0;
|
|
xv = u->xchange/2;
|
|
yv = u->ychange/2;
|
|
zv = u->zchange/2;
|
|
|
|
u->ret = move_missile(Weapon, xv, yv, zv, Z(16), Z(16), CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
}
|
|
#endif
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
char hit_burn = 0;
|
|
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
SPRITEp hsp;
|
|
USERp hu;
|
|
|
|
hsp = &sprite[NORM_SPRITE(u->ret)];
|
|
hu = User[NORM_SPRITE(u->ret)];
|
|
|
|
if (TEST(hsp->extra, SPRX_BURNABLE))
|
|
{
|
|
if (!hu)
|
|
hu = SpawnUser(hsp - sprite, hsp->picnum, NULL);
|
|
SpawnFireballFlames(Weapon, hsp - sprite);
|
|
hit_burn = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hit_burn)
|
|
{
|
|
if (u->ID == GORO_FIREBALL)
|
|
SpawnGoroFireballExp(Weapon);
|
|
else
|
|
SpawnFireballExp(Weapon);
|
|
}
|
|
|
|
KillSprite(Weapon);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
int
|
|
DoFindGround(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], hsp;
|
|
USERp u = User[SpriteNum];
|
|
int ceilhit, florhit;
|
|
short save_cstat;
|
|
short bak_cstat;
|
|
|
|
// recursive routine to find the ground - either sector or floor sprite
|
|
// skips over enemy and other types of sprites
|
|
|
|
save_cstat = sp->cstat;
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
FAFgetzrange(sp->x, sp->y, sp->z, sp->sectnum, &u->hiz, &ceilhit, &u->loz, &florhit, (((int) sp->clipdist) << 2) - GETZRANGE_CLIP_ADJ, CLIPMASK_PLAYER);
|
|
sp->cstat = save_cstat;
|
|
|
|
ASSERT(TEST(florhit, HIT_SPRITE | HIT_SECTOR));
|
|
|
|
switch (TEST(florhit, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
hsp = &sprite[NORM_SPRITE(florhit)];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR))
|
|
{
|
|
// found a sprite floor
|
|
u->lo_sp = hsp;
|
|
u->lo_sectp = NULL;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// reset the blocking bit of what you hit and try again -
|
|
// recursive
|
|
bak_cstat = hsp->cstat;
|
|
RESET(hsp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
DoFindGround(SpriteNum);
|
|
hsp->cstat = bak_cstat;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
case HIT_SECTOR:
|
|
{
|
|
u->lo_sectp = §or[NORM_SECTOR(florhit)];
|
|
u->lo_sp = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
default:
|
|
ASSERT(TRUE == FALSE);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoFindGroundPoint(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], hsp;
|
|
USERp u = User[SpriteNum];
|
|
int ceilhit, florhit;
|
|
short save_cstat;
|
|
short bak_cstat;
|
|
|
|
// recursive routine to find the ground - either sector or floor sprite
|
|
// skips over enemy and other types of sprites
|
|
|
|
save_cstat = sp->cstat;
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
FAFgetzrangepoint(sp->x, sp->y, sp->z, sp->sectnum, &u->hiz, &ceilhit, &u->loz, &florhit);
|
|
sp->cstat = save_cstat;
|
|
|
|
ASSERT(TEST(florhit, HIT_SPRITE | HIT_SECTOR));
|
|
|
|
switch (TEST(florhit, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
hsp = &sprite[NORM_SPRITE(florhit)];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR))
|
|
{
|
|
// found a sprite floor
|
|
u->lo_sp = hsp;
|
|
u->lo_sectp = NULL;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// reset the blocking bit of what you hit and try again -
|
|
// recursive
|
|
bak_cstat = hsp->cstat;
|
|
RESET(hsp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
DoFindGroundPoint(SpriteNum);
|
|
hsp->cstat = bak_cstat;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
case HIT_SECTOR:
|
|
{
|
|
u->lo_sectp = §or[NORM_SECTOR(florhit)];
|
|
u->lo_sp = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
default:
|
|
ASSERT(TRUE == FALSE);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoNapalm(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon], exp;
|
|
USERp u = User[Weapon];
|
|
short explosion;
|
|
int ox, oy, oz;
|
|
|
|
DoBlurExtend(Weapon, 1, 7);
|
|
|
|
u = User[Weapon];
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
sp->xrepeat = sp->yrepeat -= 1;
|
|
if (sp->xrepeat <= 30)
|
|
{
|
|
SpawnSmokePuff(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ox = sp->x;
|
|
oy = sp->y;
|
|
oz = sp->z;
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
// this sprite is suPlayerosed to go through players/enemys
|
|
// if hit a player/enemy back up and do it again with blocking reset
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
SPRITEp hsp = &sprite[NORM_SPRITE(u->ret)];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_BLOCK) && !TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
short hcstat = hsp->cstat;
|
|
|
|
sp->x = ox;
|
|
sp->y = oy;
|
|
sp->z = oz;
|
|
|
|
RESET(hsp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
hsp->cstat = hcstat;
|
|
}
|
|
}
|
|
}
|
|
|
|
u->Counter++;
|
|
if (u->Counter > 2)
|
|
u->Counter = 0;
|
|
|
|
if (!u->Counter)
|
|
{
|
|
USERp eu;
|
|
|
|
PlaySound(DIGI_NAPPUFF, sp, v3df_none);
|
|
|
|
explosion = SpawnSprite(STAT_MISSILE, NAP_EXP, s_NapExp, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
//exp->owner = sp->owner;
|
|
SetOwner(sp->owner, explosion);
|
|
exp->shade = -40;
|
|
exp->cstat = sp->cstat;
|
|
exp->xrepeat = 48;
|
|
exp->yrepeat = 64;
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
if (RANDOM_P2(1024) < 512)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
RESET(exp->cstat, CSTAT_SPRITE_TRANSLUCENT);
|
|
eu->Radius = 1500;
|
|
|
|
DoFindGroundPoint(explosion);
|
|
MissileWaterAdjust(explosion);
|
|
exp->z = eu->loz;
|
|
eu->oz = exp->z;
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(eu->Flags, SPR_UNDERWATER);
|
|
|
|
ASSERT(exp->picnum == 3072);
|
|
ASSERT(eu->Tics == 0);
|
|
}
|
|
|
|
// DoDamageTest(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
KillSprite((short) Weapon);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define WORM 1
|
|
|
|
#if WORM == 1
|
|
int
|
|
DoBloodWorm(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
short ang;
|
|
int xvect,yvect;
|
|
int bx,by;
|
|
int amt;
|
|
short sectnum;
|
|
|
|
u = User[Weapon];
|
|
|
|
u->ret = move_ground_missile(Weapon, u->xchange, u->ychange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (u->ret)
|
|
{
|
|
u->xchange = -u->xchange;
|
|
u->ychange = -u->ychange;
|
|
u->ret = 0;
|
|
sp->ang = NORM_ANGLE(sp->ang + 1024);
|
|
return TRUE;
|
|
}
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (!u->z_tgt)
|
|
{
|
|
// stay alive for 10 seconds
|
|
if (++u->Counter3 > 3)
|
|
{
|
|
SPRITEp tsp;
|
|
USERp tu;
|
|
int i,nexti;
|
|
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
|
|
// Kill any old zombies you own
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti)
|
|
{
|
|
tsp = &sprite[i];
|
|
tu = User[i];
|
|
|
|
ASSERT(tu);
|
|
|
|
if (tu->ID == ZOMBIE_RUN_R0 && tsp->owner == sp->owner)
|
|
{
|
|
InitBloodSpray(i,TRUE,105);
|
|
InitBloodSpray(i,TRUE,105);
|
|
SetSuicide(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SpawnZombie2(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ang = NORM_ANGLE(sp->ang + 512);
|
|
|
|
xvect = sintable[NORM_ANGLE(ang+512)];
|
|
yvect = sintable[ang];
|
|
|
|
bx = sp->x;
|
|
by = sp->y;
|
|
|
|
amt = RANDOM_P2(2048) - 1024;
|
|
sp->x += mulscale15(amt,xvect);
|
|
sp->y += mulscale15(amt,yvect);
|
|
|
|
sectnum = sp->sectnum;
|
|
updatesectorz(sp->x, sp->y, sp->z, §num);
|
|
if (sectnum >= 0)
|
|
{
|
|
GlobalSkipZrange = TRUE;
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
GlobalSkipZrange = FALSE;
|
|
}
|
|
|
|
sp->x = bx;
|
|
sp->y = by;
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
#if WORM == 2
|
|
int
|
|
DoBloodWorm(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon], exp;
|
|
USERp u = User[Weapon];
|
|
int offset;
|
|
short ang;
|
|
int x,y,z,xvect,yvect;
|
|
int bx,by;
|
|
int amt;
|
|
short sectnum;
|
|
|
|
u = User[Weapon];
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
// stay on ground
|
|
sp->z = u->loz - Z(32);
|
|
|
|
if (u->ret)
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
|
|
case HIT_PLAX_WALL:
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
|
|
case HIT_SPRITE:
|
|
case HIT_WALL:
|
|
{
|
|
u->xchange = -u->xchange;
|
|
u->ychange = -u->ychange;
|
|
u->ret = 0;
|
|
sp->ang = NORM_ANGLE(sp->ang + 1024);
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
sp->z -= Z(32);
|
|
|
|
u->xchange = -u->xchange;
|
|
u->ychange = -u->ychange;
|
|
u->ret = 0;
|
|
sp->ang = NORM_ANGLE(sp->ang + 1024);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (++u->Counter3 > 3)
|
|
{
|
|
SPRITEp tsp;
|
|
USERp tu;
|
|
int i,nexti;
|
|
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
|
|
// Kill any old zombies you own
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti)
|
|
{
|
|
tsp = &sprite[i];
|
|
tu = User[i];
|
|
|
|
ASSERT(tu);
|
|
|
|
if (tu->ID == ZOMBIE_RUN_R0 && tsp->owner == sp->owner)
|
|
{
|
|
InitBloodSpray(i,TRUE,105);
|
|
InitBloodSpray(i,TRUE,105);
|
|
SetSuicide(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SpawnZombie2(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
ang = NORM_ANGLE(sp->ang + 512);
|
|
|
|
xvect = sintable[NORM_ANGLE(ang+512)];
|
|
yvect = sintable[ang];
|
|
|
|
bx = sp->x;
|
|
by = sp->y;
|
|
|
|
amt = RANDOM_P2(2048) - 1024;
|
|
sp->x += mulscale15(amt,xvect);
|
|
sp->y += mulscale15(amt,yvect);
|
|
|
|
sectnum = sp->sectnum;
|
|
updatesectorz(sp->x, sp->y, sp->z, §num);
|
|
if (sectnum >= 0)
|
|
{
|
|
//GlobalSkipZrange = TRUE;
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
//GlobalSkipZrange = FALSE;
|
|
}
|
|
|
|
if (RANDOM_P2(2048) < 512)
|
|
{
|
|
sp->x = bx;
|
|
sp->y = by;
|
|
|
|
amt = RANDOM_P2(2048) - 1024;
|
|
sp->x += mulscale15(amt,xvect);
|
|
sp->y += mulscale15(amt,yvect);
|
|
|
|
sectnum = sp->sectnum;
|
|
updatesectorz(sp->x, sp->y, sp->z, §num);
|
|
if (sectnum >= 0)
|
|
{
|
|
//GlobalSkipZrange = TRUE;
|
|
InitBloodSpray(Weapon, FALSE, 1);
|
|
//GlobalSkipZrange = FALSE;
|
|
}
|
|
}
|
|
|
|
sp->x = bx;
|
|
sp->y = by;
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
DoMeteor(int16_t Weapon)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoSerpMeteor(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int ox, oy, oz;
|
|
|
|
ox = sp->x;
|
|
oy = sp->y;
|
|
oz = sp->z;
|
|
|
|
sp->xrepeat += MISSILEMOVETICS * 2;
|
|
if (sp->xrepeat > 80)
|
|
sp->xrepeat = 80;
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (u->ret)
|
|
{
|
|
// this sprite is supposed to go through players/enemys
|
|
// if hit a player/enemy back up and do it again with blocking reset
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
SPRITEp hsp = &sprite[NORM_SPRITE(u->ret)];
|
|
USERp hu = User[NORM_SPRITE(u->ret)];
|
|
|
|
if (hu && hu->ID >= SKULL_R0 && hu->ID <= SKULL_SERP)
|
|
{
|
|
short hcstat = hsp->cstat;
|
|
|
|
sp->x = ox;
|
|
sp->y = oy;
|
|
sp->z = oz;
|
|
|
|
RESET(hsp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
hsp->cstat = hcstat;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnMeteorExp(Weapon);
|
|
|
|
KillSprite((short) Weapon);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
int
|
|
DoMirvMissile(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
|
|
u = User[Weapon];
|
|
|
|
sp->xrepeat += MISSILEMOVETICS * 2;
|
|
if (sp->xrepeat > 80)
|
|
sp->xrepeat = 80;
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
if (u->ret)
|
|
{
|
|
if (WeaponMoveHit(Weapon))
|
|
{
|
|
SpawnMeteorExp(Weapon);
|
|
|
|
KillSprite((short) Weapon);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
int
|
|
DoMirv(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon], np;
|
|
USERp u = User[Weapon], nu;
|
|
short New;
|
|
// int ox, oy, oz;
|
|
|
|
u = User[Weapon];
|
|
|
|
// ox = sp->x;
|
|
// oy = sp->y;
|
|
// oz = sp->z;
|
|
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
|
|
MissileHitDiveArea(Weapon);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) && (RANDOM_P2(1024 << 4) >> 4) < 256)
|
|
SpawnBubble(Weapon);
|
|
|
|
#if 0
|
|
if (u->ret)
|
|
{
|
|
// this sprite is supposed to go through players/enemys
|
|
// if hit a player/enemy back up and do it again with blocking reset
|
|
if (TEST(u->ret, HIT_MASK) == HIT_SPRITE)
|
|
{
|
|
SPRITEp hsp = &sprite[NORM_SPRITE(u->ret)];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_BLOCK) && !TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
short hcstat = hsp->cstat;
|
|
|
|
sp->x = ox;
|
|
sp->y = oy;
|
|
sp->z = oz;
|
|
|
|
RESET(hsp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
u->ret = move_missile(Weapon, u->xchange, u->ychange, u->zchange, u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS);
|
|
hsp->cstat = hcstat;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
u->Counter++;
|
|
u->Counter &= 1;
|
|
|
|
if (!u->Counter)
|
|
{
|
|
int i;
|
|
static short angs[] =
|
|
{
|
|
512,
|
|
-512
|
|
};
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
New = SpawnSprite(STAT_MISSILE, MIRV_METEOR, &sg_MirvMeteor[0][0], sp->sectnum,
|
|
sp->x, sp->y, sp->z, NORM_ANGLE(sp->ang + angs[i]), 800);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
nu->RotNum = 5;
|
|
NewStateGroup(New, &sg_MirvMeteor[0]);
|
|
nu->StateEnd = s_MirvMeteorExp;
|
|
|
|
//np->owner = Weapon;
|
|
SetOwner(Weapon, New);
|
|
np->shade = -40;
|
|
np->xrepeat = 40;
|
|
np->yrepeat = 40;
|
|
np->clipdist = 32L >> 2;
|
|
np->zvel = 0;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
nu->ceiling_dist = Z(16);
|
|
nu->floor_dist = Z(16);
|
|
nu->Dist = 200;
|
|
//nu->Dist = 0;
|
|
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
nu->zchange = np->zvel;
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
|
|
}
|
|
|
|
if (u->ret)
|
|
{
|
|
SpawnMeteorExp(Weapon);
|
|
//SpawnBasicExp(Weapon);
|
|
KillSprite((short) Weapon);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SWBOOL
|
|
MissileSetPos(short Weapon, ANIMATORp DoWeapon, int dist)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
int oldvel, oldzvel;
|
|
int oldxc, oldyc, oldzc;
|
|
char retval = FALSE;
|
|
|
|
// backup values
|
|
oldxc = wu->xchange;
|
|
oldyc = wu->ychange;
|
|
oldzc = wu->zchange;
|
|
oldvel = wp->xvel;
|
|
oldzvel = wp->zvel;
|
|
|
|
// make missile move in smaller increments
|
|
wp->xvel = (dist * 6L) / MISSILEMOVETICS;
|
|
//wp->zvel = (wp->zvel*4) / MISSILEMOVETICS;
|
|
wp->zvel = (wp->zvel*6L) / MISSILEMOVETICS;
|
|
|
|
// some Weapon Animators use this
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
SET(wu->Flags, SPR_SET_POS_DONT_KILL);
|
|
if ((*DoWeapon)(Weapon))
|
|
retval = TRUE;
|
|
RESET(wu->Flags, SPR_SET_POS_DONT_KILL);
|
|
|
|
// reset values
|
|
wu->xchange = oldxc;
|
|
wu->ychange = oldyc;
|
|
wu->zchange = oldzc;
|
|
wp->xvel = oldvel;
|
|
wp->zvel = oldzvel;
|
|
|
|
// update for interpolation
|
|
wu->ox = wp->x;
|
|
wu->oy = wp->y;
|
|
wu->oz = wp->z;
|
|
|
|
return retval;
|
|
}
|
|
|
|
SWBOOL
|
|
TestMissileSetPos(short Weapon, ANIMATORp DoWeapon, int dist, int zvel)
|
|
{
|
|
SPRITEp wp = &sprite[Weapon];
|
|
USERp wu = User[Weapon];
|
|
int oldvel, oldzvel;
|
|
int oldxc, oldyc, oldzc;
|
|
char retval = FALSE;
|
|
|
|
// backup values
|
|
oldxc = wu->xchange;
|
|
oldyc = wu->ychange;
|
|
oldzc = wu->zchange;
|
|
oldvel = wp->xvel;
|
|
oldzvel = wp->zvel;
|
|
|
|
// make missile move in smaller increments
|
|
wp->xvel = (dist * 6L) / MISSILEMOVETICS;
|
|
//wp->zvel = (wp->zvel*4) / MISSILEMOVETICS;
|
|
zvel = (zvel*6L) / MISSILEMOVETICS;
|
|
|
|
// some Weapon Animators use this
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
SET(wu->Flags, SPR_SET_POS_DONT_KILL);
|
|
if ((*DoWeapon)(Weapon))
|
|
retval = TRUE;
|
|
RESET(wu->Flags, SPR_SET_POS_DONT_KILL);
|
|
|
|
// reset values
|
|
wu->xchange = oldxc;
|
|
wu->ychange = oldyc;
|
|
wu->zchange = oldzc;
|
|
wp->xvel = oldvel;
|
|
wp->zvel = oldzvel;
|
|
|
|
// update for interpolation
|
|
wu->ox = wp->x;
|
|
wu->oy = wp->y;
|
|
wu->oz = wp->z;
|
|
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
DoRing(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
PLAYERp pp = User[sp->owner]->PlayerP;
|
|
SPRITEp so = &sprite[sp->owner];
|
|
int cz,fz;
|
|
|
|
#define RINGMOVETICS (MISSILEMOVETICS * 2)
|
|
#define RING_OUTER_DIST 3200
|
|
#define RING_INNER_DIST 800
|
|
|
|
ASSERT(sp->owner != -1);
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
sp->xrepeat = sp->yrepeat -= 1;
|
|
if (sp->xrepeat <= 30)
|
|
{
|
|
SpawnSmokePuff(Weapon);
|
|
KillSprite(Weapon);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// move the center with the player
|
|
sp->x = sprite[sp->owner].x;
|
|
sp->y = sprite[sp->owner].y;
|
|
if (User[sp->owner]->PlayerP)
|
|
sp->z = pp->posz + Z(20);
|
|
else
|
|
sp->z = SPRITEp_MID(so) + Z(30);
|
|
|
|
// go out until its time to come back in
|
|
if (u->Counter2 == FALSE)
|
|
{
|
|
u->Dist += 8 * RINGMOVETICS;
|
|
|
|
if (u->Dist > RING_OUTER_DIST)
|
|
{
|
|
u->Counter2 = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u->Dist -= 8 * RINGMOVETICS;
|
|
|
|
if (u->Dist <= RING_INNER_DIST)
|
|
{
|
|
if (!User[sp->owner]->PlayerP)
|
|
User[sp->owner]->Counter--;
|
|
KillSprite(Weapon);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//sp->ang = NORM_ANGLE(sp->ang - 512);
|
|
|
|
// rotate the ring
|
|
sp->ang = NORM_ANGLE(sp->ang + (4 * RINGMOVETICS) + RINGMOVETICS);
|
|
|
|
// put it out there
|
|
sp->x += ((int) u->Dist * (int) sintable[NORM_ANGLE(sp->ang + 512)]) >> 14;
|
|
sp->y += ((int) u->Dist * (int) sintable[sp->ang]) >> 14;
|
|
if (User[sp->owner]->PlayerP)
|
|
sp->z += (u->Dist * ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT)) >> 9;
|
|
|
|
//sp->ang = NORM_ANGLE(sp->ang + 512);
|
|
//updatesector(sp->x, sp->y);
|
|
|
|
setsprite(Weapon, (vec3_t *)sp);
|
|
|
|
ASSERT(sp->sectnum >= 0);
|
|
|
|
getzsofslope(sp->sectnum, sp->x, sp->y, &cz, &fz);
|
|
|
|
// bound the sprite by the sectors ceiling and floor
|
|
if (sp->z > fz)
|
|
{
|
|
sp->z = fz;
|
|
}
|
|
|
|
if (sp->z < cz + SPRITEp_SIZE_Z(sp))
|
|
{
|
|
sp->z = cz + SPRITEp_SIZE_Z(sp);
|
|
}
|
|
|
|
// Done last - check for damage
|
|
DoDamageTest(Weapon);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
InitSpellRing(PLAYERp pp)
|
|
{
|
|
short ang, ang_diff, ang_start, SpriteNum, missiles;
|
|
SPRITEp sp;
|
|
USERp u;
|
|
short max_missiles = 16;
|
|
short ammo;
|
|
|
|
ammo = NAPALM_MIN_AMMO;
|
|
|
|
if (pp->WpnAmmo[WPN_HOTHEAD] < ammo)
|
|
return;
|
|
else
|
|
PlayerUpdateAmmo(pp, WPN_HOTHEAD, -ammo);
|
|
|
|
ang_diff = 2048 / max_missiles;
|
|
|
|
ang_start = NORM_ANGLE(FixedToInt(pp->q16ang) - DIV2(2048));
|
|
|
|
if (!SW_SHAREWARE)
|
|
PlaySound(DIGI_RFWIZ, pp, v3df_none);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return;
|
|
|
|
for (missiles = 0, ang = ang_start; missiles < max_missiles; ang += ang_diff, missiles++)
|
|
{
|
|
SpriteNum = SpawnSprite(STAT_MISSILE_SKIP4, FIREBALL1, s_Ring, pp->cursectnum, pp->posx, pp->posy, pp->posz, ang, 0);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
|
|
sp->hitag = LUMINOUS; //Always full brightness
|
|
sp->xvel = 500;
|
|
//sp->owner = pp->SpriteP - sprite;
|
|
SetOwner(pp->SpriteP - sprite, SpriteNum);
|
|
sp->shade = -40;
|
|
sp->xrepeat = 32;
|
|
sp->yrepeat = 32;
|
|
sp->zvel = 0;
|
|
|
|
u = User[SpriteNum];
|
|
|
|
u->sz = Z(20);
|
|
u->Dist = RING_INNER_DIST;
|
|
u->Counter = max_missiles;
|
|
u->Counter2 = 0;
|
|
u->ceiling_dist = Z(10);
|
|
u->floor_dist = Z(10);
|
|
|
|
//u->RotNum = 5;
|
|
//NewStateGroup(SpriteNum, &sg_Ring);
|
|
//SET(u->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
// put it out there
|
|
sp->x += ((int) u->Dist * (int) sintable[NORM_ANGLE(sp->ang + 512)]) >> 14;
|
|
sp->y += ((int) u->Dist * (int) sintable[sp->ang]) >> 14;
|
|
sp->z = pp->posz + Z(20) + ((u->Dist * ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT)) >> 9);
|
|
|
|
sp->ang = NORM_ANGLE(sp->ang + 512);
|
|
|
|
u->ox = sp->x;
|
|
u->oy = sp->y;
|
|
u->oz = sp->z;
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(sp))
|
|
SET(u->Flags, SPR_UNDERWATER);
|
|
}
|
|
}
|
|
|
|
int
|
|
DoSerpRing(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
USERp ou = User[sp->owner];
|
|
int dist,a,b,c;
|
|
int cz,fz;
|
|
|
|
// if owner does not exist or he's dead on the floor
|
|
// kill off all of his skull children
|
|
if (sp->owner == -1 || ou->RotNum < 5)
|
|
{
|
|
UpdateSinglePlayKills(Weapon);
|
|
DoSkullBeginDeath(Weapon);
|
|
// +2 does not spawn shrapnel
|
|
u->ID = SKULL_SERP;
|
|
return 0;
|
|
}
|
|
|
|
// move the center with the player
|
|
sp->x = sprite[sp->owner].x;
|
|
sp->y = sprite[sp->owner].y;
|
|
|
|
sp->z += sp->zvel;
|
|
if (sp->z > sprite[sp->owner].z - u->sz)
|
|
sp->z = sprite[sp->owner].z - u->sz;
|
|
|
|
// go out until its time to come back in
|
|
if (u->Counter2 == FALSE)
|
|
{
|
|
u->Dist += 8 * RINGMOVETICS;
|
|
|
|
if (u->Dist > u->TargetDist)
|
|
u->Counter2 = TRUE;
|
|
}
|
|
|
|
// rotate the ring
|
|
u->slide_ang = NORM_ANGLE(u->slide_ang + sp->yvel);
|
|
|
|
// rotate the heads
|
|
if (TEST(u->Flags, SPR_BOUNCE))
|
|
sp->ang = NORM_ANGLE(sp->ang + (28 * RINGMOVETICS));
|
|
else
|
|
sp->ang = NORM_ANGLE(sp->ang - (28 * RINGMOVETICS));
|
|
|
|
// put it out there
|
|
sp->x += ((int) u->Dist * (int) sintable[NORM_ANGLE(u->slide_ang + 512)]) >> 14;
|
|
sp->y += ((int) u->Dist * (int) sintable[u->slide_ang]) >> 14;
|
|
|
|
setsprite(Weapon, (vec3_t *)sp);
|
|
|
|
ASSERT(sp->sectnum >= 0);
|
|
|
|
getzsofslope(sp->sectnum, sp->x, sp->y, &cz, &fz);
|
|
|
|
// bound the sprite by the sectors ceiling and floor
|
|
if (sp->z > fz)
|
|
{
|
|
sp->z = fz;
|
|
}
|
|
|
|
if (sp->z < cz + SPRITEp_SIZE_Z(sp))
|
|
{
|
|
sp->z = cz + SPRITEp_SIZE_Z(sp);
|
|
}
|
|
|
|
if (u->Counter2 > 0)
|
|
{
|
|
if (!User[ou->tgt_sp-sprite] ||
|
|
!User[ou->tgt_sp-sprite]->PlayerP ||
|
|
!TEST(User[ou->tgt_sp-sprite]->PlayerP->Flags, PF_DEAD))
|
|
{
|
|
u->tgt_sp = ou->tgt_sp;
|
|
DISTANCE(sp->x, sp->y, u->tgt_sp->x, u->tgt_sp->y, dist, a,b,c);
|
|
|
|
// if ((dist ok and random ok) OR very few skulls left)
|
|
if ((dist < 18000 && (RANDOM_P2(2048<<5)>>5) < 16) || User[sp->owner]->Counter < 4)
|
|
{
|
|
short sectnum = sp->sectnum;
|
|
COVERupdatesector(sp->x,sp->y,§num);
|
|
|
|
// if (valid sector and can see target)
|
|
if (sectnum != -1 && CanSeePlayer(Weapon))
|
|
{
|
|
extern STATEp sg_SkullJump[];
|
|
u->ID = SKULL_R0;
|
|
sp->ang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
sp->xvel = dist>>5;
|
|
sp->xvel += DIV2(sp->xvel);
|
|
sp->xvel += (RANDOM_P2(128<<8)>>8);
|
|
u->jump_speed = -800;
|
|
change_sprite_stat(Weapon, STAT_ENEMY);
|
|
NewStateGroup(Weapon, sg_SkullJump);
|
|
DoBeginJump(Weapon);
|
|
// tell owner that one is gone
|
|
// User[sp->owner]->Counter--;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Done last - check for damage
|
|
//DoDamageTest(Weapon);
|
|
|
|
// if its exploded
|
|
#if 0
|
|
if (u->RotNum == 0)
|
|
{
|
|
// tell owner that one is gone
|
|
User[sp->owner]->Counter--;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitLavaFlame(short SpriteNum)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitLavaThrow(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
//PlaySound(DIGI_NINJAROCKETATTACK, sp, v3df_none);
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = SPRITEp_TOS(sp) + DIV4(SPRITEp_SIZE_Z(sp));
|
|
|
|
// Spawn a shot
|
|
w = SpawnSprite(STAT_MISSILE, LAVA_BOULDER, s_LavaBoulder, sp->sectnum,
|
|
nx, ny, nz, nang, NINJA_BOLT_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
wp->yrepeat = 72;
|
|
wp->xrepeat = 72;
|
|
wp->shade = -15;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(wp->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(wp->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
wu->Radius = 200;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 256>>2;
|
|
wu->ceiling_dist = Z(14);
|
|
wu->floor_dist = Z(14);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
MissileSetPos(w, DoLavaBoulder, 1200);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
return w;
|
|
}
|
|
|
|
int
|
|
InitVulcanBoulder(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp wu;
|
|
int nx, ny, nz, nang;
|
|
short w;
|
|
int zsize;
|
|
int zvel, zvel_rand;
|
|
short delta;
|
|
short vel;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - Z(40);
|
|
|
|
if (SP_TAG7(sp))
|
|
{
|
|
delta = SP_TAG5(sp);
|
|
nang = sp->ang + (RANDOM_RANGE(delta) - DIV2(delta));
|
|
nang = NORM_ANGLE(nang);
|
|
}
|
|
else
|
|
{
|
|
nang = RANDOM_P2(2048);
|
|
}
|
|
|
|
if (SP_TAG6(sp))
|
|
vel = SP_TAG6(sp);
|
|
else
|
|
vel = 800;
|
|
|
|
// Spawn a shot
|
|
w = SpawnSprite(STAT_MISSILE, LAVA_BOULDER, s_VulcanBoulder, sp->sectnum,
|
|
nx, ny, nz, nang, (vel/2 + vel/4) + RANDOM_RANGE(vel/4));
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->xrepeat = wp->yrepeat = 8 + RANDOM_RANGE(72);
|
|
wp->shade = -40;
|
|
wp->ang = nang;
|
|
wu->Counter = 0;
|
|
|
|
zsize = SPRITEp_SIZE_Z(wp);
|
|
|
|
wu->Radius = 200;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 256>>2;
|
|
wu->ceiling_dist = zsize/2;
|
|
wu->floor_dist = zsize/2;
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(wp->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024) > 512)
|
|
SET(wp->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
if (SP_TAG7(sp))
|
|
{
|
|
zvel = SP_TAG7(sp);
|
|
zvel_rand = SP_TAG8(sp);
|
|
}
|
|
else
|
|
{
|
|
zvel = 50;
|
|
zvel_rand = 40;
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = -Z(zvel) + -Z(RANDOM_RANGE(zvel_rand));
|
|
|
|
return w;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
InitSerpRing(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, np;
|
|
USERp u = User[SpriteNum], nu;
|
|
short ang, ang_diff, ang_start, ang_finish, missiles, NewSprite;
|
|
short max_missiles = 16;
|
|
|
|
u->Counter = max_missiles;
|
|
|
|
ang_diff = 2048 / max_missiles;
|
|
|
|
ang_start = NORM_ANGLE(sp->ang - DIV2(2048));
|
|
|
|
if (!SW_SHAREWARE)
|
|
PlaySound(DIGI_RFWIZ, sp, v3df_none);
|
|
|
|
for (missiles = 0, ang = ang_start; missiles < max_missiles; ang += ang_diff, missiles++)
|
|
{
|
|
NewSprite = SpawnSprite(STAT_MISSILE_SKIP4, FIREBALL1, s_Ring2, sp->sectnum, sp->x, sp->y, SPRITEp_TOS(sp)+Z(20), ang, 0);
|
|
|
|
np = &sprite[NewSprite];
|
|
|
|
np->hitag = LUMINOUS; //Always full brightness
|
|
np->xvel = 500;
|
|
SetOwner(SpriteNum, NewSprite);
|
|
np->shade = -40;
|
|
np->xrepeat = 32;
|
|
np->yrepeat = 32;
|
|
np->zvel = 0;
|
|
|
|
nu = User[NewSprite];
|
|
|
|
nu->sz = Z(20);
|
|
nu->Dist = 800;
|
|
nu->Counter = max_missiles;
|
|
nu->Counter2 = 0;
|
|
nu->ceiling_dist = Z(10);
|
|
nu->floor_dist = Z(10);
|
|
nu->spal = np->pal = 27; // Bright Green
|
|
|
|
// put it out there
|
|
np->x += ((int) nu->Dist * (int) sintable[NORM_ANGLE(np->ang + 512)]) >> 14;
|
|
np->y += ((int) nu->Dist * (int) sintable[np->ang]) >> 14;
|
|
np->z = SPRITEp_TOS(sp) + Z(20);
|
|
|
|
np->ang = NORM_ANGLE(np->ang + 512);
|
|
|
|
nu->ox = np->x;
|
|
nu->oy = np->y;
|
|
nu->oz = np->z;
|
|
|
|
if (SpriteInUnderwaterArea(sp))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
}
|
|
#else
|
|
int
|
|
InitSerpRing(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, np;
|
|
USERp u = User[SpriteNum], nu;
|
|
short ang, ang_diff, ang_start, missiles, New;
|
|
short max_missiles;
|
|
|
|
#define SERP_RING_DIST 2800 // Was 3500
|
|
|
|
extern STATE s_SkullExplode[];
|
|
extern STATE s_SkullRing[5][1];
|
|
extern STATEp sg_SkullRing[];
|
|
|
|
max_missiles = 12;
|
|
|
|
u->Counter = max_missiles;
|
|
|
|
ang_diff = 2048 / max_missiles;
|
|
|
|
ang_start = NORM_ANGLE(sp->ang - DIV2(2048));
|
|
|
|
PlaySound(DIGI_SERPSUMMONHEADS, sp, v3df_none);
|
|
|
|
for (missiles = 0, ang = ang_start; missiles < max_missiles; ang += ang_diff, missiles++)
|
|
{
|
|
New = SpawnSprite(STAT_SKIP4, SKULL_SERP, &s_SkullRing[0][0], sp->sectnum, sp->x, sp->y, sp->z, ang, 0);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->xvel = 500;
|
|
//np->owner = SpriteNum;
|
|
SetOwner(SpriteNum, New);
|
|
np->shade = -20;
|
|
np->xrepeat = 64;
|
|
np->yrepeat = 64;
|
|
np->yvel = 2*RINGMOVETICS;
|
|
np->zvel = Z(3);
|
|
np->pal = 0;
|
|
|
|
np->z = SPRITEp_TOS(sp) - Z(20);
|
|
nu->sz = Z(50);
|
|
|
|
// ang around the serp is now slide_ang
|
|
nu->slide_ang = np->ang;
|
|
// randomize the head turning angle
|
|
np->ang = RANDOM_P2(2048<<5)>>5;
|
|
|
|
// control direction of spinning
|
|
FLIP(u->Flags, SPR_BOUNCE);
|
|
SET(nu->Flags, TEST(u->Flags, SPR_BOUNCE));
|
|
|
|
nu->Dist = 600;
|
|
nu->TargetDist = SERP_RING_DIST;
|
|
nu->Counter2 = 0;
|
|
|
|
nu->StateEnd = s_SkullExplode;
|
|
nu->Rot = sg_SkullRing;
|
|
|
|
// defaults do change the statnum
|
|
EnemyDefaults(New, NULL, NULL);
|
|
change_sprite_stat(New, STAT_SKIP4);
|
|
RESET(np->extra, SPRX_PLAYER_OR_ENEMY);
|
|
|
|
np->clipdist = (128+64) >> 2;
|
|
SET(nu->Flags, SPR_XFLIP_TOGGLE);
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
nu->Radius = 400;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
int
|
|
InitSerpRing2(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, np;
|
|
USERp u = User[SpriteNum], nu;
|
|
short ang, ang_diff, ang_start, ang_finish, missiles, New;
|
|
short max_missiles;
|
|
short i;
|
|
|
|
extern STATE s_SkullExplode[];
|
|
extern STATE s_SkullRing[5][1];
|
|
extern STATEp sg_SkullRing[];
|
|
|
|
#define NUM_SERP_RING 2
|
|
|
|
static int zpos[NUM_SERP_RING] = {Z(25), Z(130)};
|
|
|
|
PlaySound(DIGI_SERPSUMMONHEADS, sp, v3df_none);
|
|
|
|
for (i = 0; i < NUM_SERP_RING; i++)
|
|
{
|
|
max_missiles = 12;
|
|
|
|
ang_diff = 2048 / max_missiles;
|
|
|
|
ang_start = i * (2048/NUM_SERP_RING); //NORM_ANGLE(sp->ang - DIV2(2048));
|
|
|
|
for (missiles = 0, ang = ang_start; missiles < max_missiles; ang += ang_diff, missiles++)
|
|
{
|
|
New = SpawnSprite(STAT_MISSILE_SKIP4, SKULL_SERP, &s_SkullRing[0][0], sp->sectnum, sp->x, sp->y, sp->z, ang, 0);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
//np->owner = SpriteNum;
|
|
SetOwner(SpriteNum, New);
|
|
np->shade = -20;
|
|
np->xrepeat = 64;
|
|
np->yrepeat = 64;
|
|
np->zvel = Z(10);
|
|
np->yvel = 4*RINGMOVETICS;
|
|
np->pal = 0;
|
|
|
|
np->z = SPRITEp_TOS(sp) - Z(20);
|
|
nu->sz = zpos[i]; //Z(20) + (i * (SPRITEp_SIZE_Z(sp)/NUM_SERP_RING));
|
|
|
|
// ang around the serp is now slide_ang
|
|
nu->slide_ang = np->ang;
|
|
// randomize the head turning angle
|
|
np->ang = RANDOM_P2(2048<<5)>>5;
|
|
|
|
// control direction of spinning
|
|
FLIP(u->Flags, SPR_BOUNCE);
|
|
SET(nu->Flags, TEST(u->Flags, SPR_BOUNCE));
|
|
|
|
nu->Dist = 400;
|
|
nu->TargetDist = 2500;
|
|
nu->Counter2 = 0;
|
|
|
|
nu->StateEnd = s_SkullExplode;
|
|
nu->Rot = sg_SkullRing;
|
|
|
|
// defaults do change the statnum
|
|
EnemyDefaults(New, NULL, NULL);
|
|
RESET(np->extra, SPRX_PLAYER_OR_ENEMY);
|
|
change_sprite_stat(New, STAT_MISSILE_SKIP4);
|
|
|
|
np->clipdist = (128+64) >> 2;
|
|
SET(nu->Flags, SPR_XFLIP_TOGGLE);
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
nu->Radius = 400;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
InitSpellNapalm(PLAYERp pp)
|
|
{
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
USERp u;
|
|
unsigned i;
|
|
short oclipdist;
|
|
short ammo;
|
|
|
|
typedef struct
|
|
{
|
|
int dist_over, dist_out;
|
|
short ang;
|
|
} MISSILE_PLACEMENT;
|
|
|
|
static MISSILE_PLACEMENT mp[] =
|
|
{
|
|
{600 * 6, 400, 512},
|
|
{0, 1100, 0},
|
|
{600 * 6, 400, -512},
|
|
};
|
|
|
|
ammo = NAPALM_MIN_AMMO;
|
|
|
|
if (pp->WpnAmmo[WPN_HOTHEAD] < ammo)
|
|
return;
|
|
else
|
|
PlayerUpdateAmmo(pp, WPN_HOTHEAD, -ammo);
|
|
|
|
PlaySound(DIGI_NAPFIRE, pp, v3df_none);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return;
|
|
|
|
for (i = 0; i < SIZ(mp); i++)
|
|
{
|
|
SpriteNum = SpawnSprite(STAT_MISSILE, FIREBALL1, s_Napalm, pp->cursectnum,
|
|
pp->posx, pp->posy, pp->posz + Z(12), FixedToInt(pp->q16ang), NAPALM_VELOCITY*2);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
u = User[SpriteNum];
|
|
|
|
sp->hitag = LUMINOUS; //Always full brightness
|
|
|
|
if (i==0) // Only attach sound to first projectile
|
|
{
|
|
PlaySound(DIGI_NAPWIZ, sp, v3df_follow);
|
|
Set3DSoundOwner(SpriteNum);
|
|
}
|
|
|
|
//sp->owner = pp->SpriteP - sprite;
|
|
SetOwner(pp->SpriteP - sprite, SpriteNum);
|
|
sp->shade = -40;
|
|
sp->xrepeat = 32;
|
|
sp->yrepeat = 32;
|
|
sp->clipdist = 0;
|
|
sp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
SET(sp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(u->Flags2, SPR2_BLUR_TAPER_FAST);
|
|
|
|
u->floor_dist = Z(1);
|
|
u->ceiling_dist = Z(1);
|
|
u->Dist = 200;
|
|
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 1;
|
|
|
|
if (mp[i].dist_over != 0)
|
|
{
|
|
sp->ang = NORM_ANGLE(sp->ang + mp[i].ang);
|
|
HelpMissileLateral(SpriteNum, mp[i].dist_over);
|
|
sp->ang = NORM_ANGLE(sp->ang - mp[i].ang);
|
|
}
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
u->zchange = sp->zvel;
|
|
|
|
if (MissileSetPos(SpriteNum, DoNapalm, mp[i].dist_out))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(SpriteNum);
|
|
continue;
|
|
}
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(sp))
|
|
SET(u->Flags, SPR_UNDERWATER);
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
u->Counter = 0;
|
|
|
|
}
|
|
}
|
|
|
|
int
|
|
InitEnemyNapalm(short SpriteNum)
|
|
{
|
|
short w;
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
short dist;
|
|
unsigned i;
|
|
short oclipdist;
|
|
|
|
typedef struct
|
|
{
|
|
int dist_over, dist_out;
|
|
short ang;
|
|
} MISSILE_PLACEMENT;
|
|
|
|
static MISSILE_PLACEMENT mp[] =
|
|
{
|
|
{600 * 6, 400, 512},
|
|
{0, 1100, 0},
|
|
{600 * 6, 400, -512},
|
|
};
|
|
|
|
PlaySound(DIGI_NAPFIRE, sp, v3df_none);
|
|
|
|
for (i = 0; i < SIZ(mp); i++)
|
|
{
|
|
w = SpawnSprite(STAT_MISSILE, FIREBALL1, s_Napalm, sp->sectnum,
|
|
sp->x, sp->y, SPRITEp_TOS(sp) + DIV4(SPRITEp_SIZE_Z(sp)), sp->ang, NAPALM_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
if (i==0) // Only attach sound to first projectile
|
|
{
|
|
PlaySound(DIGI_NAPWIZ, wp, v3df_follow);
|
|
Set3DSoundOwner(w);
|
|
}
|
|
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
SetOwner(sp->owner, w);
|
|
else
|
|
SetOwner(SpriteNum, w);
|
|
|
|
wp->shade = -40;
|
|
wp->xrepeat = 32;
|
|
wp->yrepeat = 32;
|
|
wp->clipdist = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(wu->Flags2, SPR2_BLUR_TAPER_FAST);
|
|
|
|
wu->floor_dist = Z(1);
|
|
wu->ceiling_dist = Z(1);
|
|
wu->Dist = 200;
|
|
|
|
oclipdist = sp->clipdist;
|
|
sp->clipdist = 1;
|
|
|
|
if (mp[i].dist_over != 0)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang + mp[i].ang);
|
|
HelpMissileLateral(w, mp[i].dist_over);
|
|
wp->ang = NORM_ANGLE(wp->ang - mp[i].ang);
|
|
}
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
MissileSetPos(w, DoNapalm, mp[i].dist_out);
|
|
|
|
sp->clipdist = oclipdist;
|
|
|
|
u->Counter = 0;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSpellMirv(PLAYERp pp)
|
|
{
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
USERp u;
|
|
short oclipdist;
|
|
|
|
PlaySound(DIGI_MIRVFIRE, pp, v3df_none);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
SpriteNum = SpawnSprite(STAT_MISSILE, FIREBALL1, s_Mirv, pp->cursectnum,
|
|
pp->posx, pp->posy, pp->posz + Z(12), FixedToInt(pp->q16ang), MIRV_VELOCITY);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
u = User[SpriteNum];
|
|
|
|
PlaySound(DIGI_MIRVWIZ, sp, v3df_follow);
|
|
Set3DSoundOwner(SpriteNum);
|
|
|
|
//sp->owner = pp->SpriteP - sprite;
|
|
SetOwner(pp->SpriteP - sprite, SpriteNum);
|
|
sp->shade = -40;
|
|
sp->xrepeat = 72;
|
|
sp->yrepeat = 72;
|
|
sp->clipdist = 32L >> 2;
|
|
sp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
SET(sp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
u->floor_dist = Z(16);
|
|
u->ceiling_dist = Z(16);
|
|
u->Dist = 200;
|
|
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
u->zchange = sp->zvel;
|
|
|
|
MissileSetPos(SpriteNum, DoMirv, 600);
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
u->Counter = 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitEnemyMirv(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
short w;
|
|
int dist;
|
|
|
|
PlaySound(DIGI_MIRVFIRE, sp, v3df_none);
|
|
|
|
w = SpawnSprite(STAT_MISSILE, MIRV_METEOR, s_Mirv, sp->sectnum,
|
|
sp->x, sp->y, SPRITEp_TOS(sp) + DIV4(SPRITEp_SIZE_Z(sp)), sp->ang, MIRV_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
PlaySound(DIGI_MIRVWIZ, wp, v3df_follow);
|
|
Set3DSoundOwner(w);
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->shade = -40;
|
|
wp->xrepeat = 72;
|
|
wp->yrepeat = 72;
|
|
wp->clipdist = 32L >> 2;
|
|
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
wu->floor_dist = Z(16);
|
|
wu->ceiling_dist = Z(16);
|
|
wu->Dist = 200;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
MissileSetPos(w, DoMirv, 600);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitSwordAttack(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite],tu;
|
|
SPRITEp sp = NULL;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist;
|
|
short reach,face;
|
|
|
|
PlaySound(DIGI_SWORDSWOOSH, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING))
|
|
{
|
|
short bubble;
|
|
SPRITEp bp;
|
|
int nx,ny;
|
|
short random_amt;
|
|
|
|
static int16_t dangs[] =
|
|
{
|
|
-256, -128, 0, 128, 256
|
|
};
|
|
|
|
for (i = 0; i < (int)SIZ(dangs); i++)
|
|
{
|
|
if (RANDOM_RANGE(1000) < 500) continue; // Don't spawn bubbles every time
|
|
bubble = SpawnBubble(pp->SpriteP - sprite);
|
|
if (bubble >= 0)
|
|
{
|
|
bp = &sprite[bubble];
|
|
|
|
bp->ang = FixedToInt(pp->q16ang);
|
|
|
|
random_amt = (RANDOM_P2(32<<8)>>8) - 16;
|
|
|
|
// back it up a bit to get it out of your face
|
|
nx = MOVEx((1024+256)*3, NORM_ANGLE(bp->ang + dangs[i] + random_amt));
|
|
ny = MOVEy((1024+256)*3, NORM_ANGLE(bp->ang + dangs[i] + random_amt));
|
|
|
|
move_missile(bubble, nx, ny, 0L, u->ceiling_dist, u->floor_dist, CLIPMASK_PLAYER, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
|
|
//if (pp->SpriteP == sp) // UnderSprite was getting hit
|
|
// break;
|
|
if (User[i]->PlayerP == pp)
|
|
break;
|
|
|
|
if (!TEST(sp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
dist = Distance(pp->posx, pp->posy, sp->x, sp->y);
|
|
|
|
reach = 1000; // !JIM! was 800
|
|
face = 200;
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, pp->SpriteP, reach) && PLAYER_FACING_RANGE(pp, sp, face))
|
|
{
|
|
if (SpriteOverlapZ(pp->PlayerSprite, i, Z(20)))
|
|
{
|
|
if (FAFcansee(sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum,pp->SpriteP->x,pp->SpriteP->y,SPRITEp_MID(pp->SpriteP),pp->SpriteP->sectnum))
|
|
DoDamage(i, pp->PlayerSprite);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// all this is to break glass
|
|
{
|
|
hitdata_t hitinfo;
|
|
short daang;
|
|
int daz;
|
|
|
|
daang = FixedToInt(pp->q16ang);
|
|
daz = ((100 - FixedToInt(pp->q16horiz)) * 2000) + (RANDOM_RANGE(24000) - 12000);
|
|
|
|
FAFhitscan(pp->posx, pp->posy, pp->posz, pp->cursectnum, // Start position
|
|
sintable[NORM_ANGLE(daang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(daang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
return 0;
|
|
|
|
if (FindDistance3D(pp->posx - hitinfo.pos.x, pp->posy - hitinfo.pos.y, pp->posz - hitinfo.pos.z) < 700)
|
|
{
|
|
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
extern STATE s_TrashCanPain[];
|
|
SPRITEp hsp = &sprite[hitinfo.sprite];
|
|
tu = User[hitinfo.sprite];
|
|
|
|
if (tu) // JBF: added null check
|
|
switch (tu->ID)
|
|
{
|
|
case ZILLA_RUN_R0:
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_SWORDCLANK, &hitinfo.pos, v3df_none);
|
|
break;
|
|
case TRASHCAN:
|
|
if (tu->WaitTics <= 0)
|
|
{
|
|
tu->WaitTics = SEC(2);
|
|
ChangeState(hitinfo.sprite,s_TrashCanPain);
|
|
}
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_SWORDCLANK, &hitinfo.pos, v3df_none);
|
|
PlaySound(DIGI_TRASHLID, sp, v3df_none);
|
|
break;
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_SWORDCLANK, &hitinfo.pos, v3df_none);
|
|
break;
|
|
}
|
|
|
|
if (sprite[hitinfo.sprite].lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_STAR, hitinfo.sprite))
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
}
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, u->ID);
|
|
}
|
|
// hit non breakable wall - do sound and puff
|
|
else
|
|
{
|
|
SpawnSwordSparks(pp, hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_SWORDCLANK, &hitinfo.pos, v3df_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitFistAttack(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite],tu;
|
|
SPRITEp sp = NULL;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist;
|
|
short reach,face;
|
|
|
|
PlaySound(DIGI_STAR, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING))
|
|
{
|
|
short bubble;
|
|
SPRITEp bp;
|
|
int nx,ny;
|
|
short random_amt;
|
|
|
|
static int16_t dangs[] =
|
|
{
|
|
-128,128
|
|
};
|
|
|
|
for (i = 0; i < (int)SIZ(dangs); i++)
|
|
{
|
|
bubble = SpawnBubble(pp->SpriteP - sprite);
|
|
if (bubble >= 0)
|
|
{
|
|
bp = &sprite[bubble];
|
|
|
|
bp->ang = FixedToInt(pp->q16ang);
|
|
|
|
random_amt = (RANDOM_P2(32<<8)>>8) - 16;
|
|
|
|
// back it up a bit to get it out of your face
|
|
nx = MOVEx((1024+256)*3, NORM_ANGLE(bp->ang + dangs[i] + random_amt));
|
|
ny = MOVEy((1024+256)*3, NORM_ANGLE(bp->ang + dangs[i] + random_amt));
|
|
|
|
move_missile(bubble, nx, ny, 0L, u->ceiling_dist, u->floor_dist, CLIPMASK_PLAYER, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
sp = &sprite[i];
|
|
|
|
//if (pp->SpriteP == sp) // UnderSprite was getting hit
|
|
// break;
|
|
if (User[i]->PlayerP == pp)
|
|
break;
|
|
|
|
if (!TEST(sp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
dist = Distance(pp->posx, pp->posy, sp->x, sp->y);
|
|
|
|
if (pp->InventoryActive[2]) // Shadow Bombs give you demon fist
|
|
{
|
|
face = 190;
|
|
reach = 2300;
|
|
}
|
|
else
|
|
{
|
|
reach = 1000;
|
|
face = 200;
|
|
}
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, pp->SpriteP, reach) && PLAYER_FACING_RANGE(pp, sp, face))
|
|
{
|
|
if (SpriteOverlapZ(pp->PlayerSprite, i, Z(20)) || face == 190)
|
|
{
|
|
if (FAFcansee(sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum,pp->SpriteP->x,pp->SpriteP->y,SPRITEp_MID(pp->SpriteP),pp->SpriteP->sectnum))
|
|
DoDamage(i, pp->PlayerSprite);
|
|
if (face == 190)
|
|
{
|
|
SpawnDemonFist(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// all this is to break glass
|
|
{
|
|
hitdata_t hitinfo;
|
|
short daang;
|
|
int daz;
|
|
|
|
daang = FixedToInt(pp->q16ang);
|
|
daz = ((100 - FixedToInt(pp->q16horiz)) * 2000) + (RANDOM_RANGE(24000) - 12000);
|
|
|
|
FAFhitscan(pp->posx, pp->posy, pp->posz, pp->cursectnum, // Start position
|
|
sintable[NORM_ANGLE(daang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(daang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
return 0;
|
|
|
|
if (FindDistance3D(pp->posx - hitinfo.pos.x, pp->posy - hitinfo.pos.y, pp->posz - hitinfo.pos.z) < 700)
|
|
{
|
|
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
extern STATE s_TrashCanPain[];
|
|
SPRITEp hsp = &sprite[hitinfo.sprite];
|
|
tu = User[hitinfo.sprite];
|
|
|
|
if (tu) // JBF: added null check
|
|
switch (tu->ID)
|
|
{
|
|
case ZILLA_RUN_R0:
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_ARMORHIT, &hitinfo.pos, v3df_none);
|
|
break;
|
|
case TRASHCAN:
|
|
if (tu->WaitTics <= 0)
|
|
{
|
|
tu->WaitTics = SEC(2);
|
|
ChangeState(hitinfo.sprite,s_TrashCanPain);
|
|
}
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_ARMORHIT, &hitinfo.pos, v3df_none);
|
|
PlaySound(DIGI_TRASHLID, sp, v3df_none);
|
|
break;
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_ARMORHIT, &hitinfo.pos, v3df_none);
|
|
break;
|
|
}
|
|
|
|
if (sprite[hitinfo.sprite].lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_STAR, hitinfo.sprite))
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
}
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
|
|
switch (hsp->picnum)
|
|
{
|
|
case 5062:
|
|
case 5063:
|
|
case 4947:
|
|
SpawnSwordSparks(pp, hitinfo.sect, -1, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_ARMORHIT, &hitinfo.pos, v3df_none);
|
|
if (RANDOM_RANGE(1000) > 700)
|
|
PlayerUpdateHealth(pp,1); // Give some health
|
|
SET(hsp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, u->ID);
|
|
}
|
|
// hit non breakable wall - do sound and puff
|
|
else
|
|
{
|
|
SpawnSwordSparks(pp, hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
PlaySound(DIGI_ARMORHIT, &hitinfo.pos, v3df_none);
|
|
if (PlayerTakeDamage(pp, -1))
|
|
{
|
|
PlayerUpdateHealth(pp, -(RANDOM_RANGE(2<<8)>>8));
|
|
PlayerCheckDeath(pp, -1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
InitSumoNapalm(short SpriteNum)
|
|
{
|
|
short w;
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
short dist;
|
|
short i,j,ang;
|
|
short oclipdist;
|
|
|
|
typedef struct
|
|
{
|
|
int dist_over, dist_out;
|
|
short ang;
|
|
} MISSILE_PLACEMENT;
|
|
|
|
static MISSILE_PLACEMENT mp[] =
|
|
{
|
|
{0, 1100, 0},
|
|
};
|
|
|
|
PlaySound(DIGI_NAPFIRE, sp, v3df_none);
|
|
|
|
ang = sp->ang;
|
|
for (j=0; j<4; j++)
|
|
{
|
|
for (i = 0; i < (int)SIZ(mp); i++)
|
|
{
|
|
w = SpawnSprite(STAT_MISSILE, FIREBALL1, s_Napalm, sp->sectnum,
|
|
sp->x, sp->y, SPRITEp_TOS(sp), ang, NAPALM_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
if (i==0) // Only attach sound to first projectile
|
|
{
|
|
PlaySound(DIGI_NAPWIZ, wp, v3df_follow);
|
|
Set3DSoundOwner(w);
|
|
}
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->shade = -40;
|
|
wp->xrepeat = 32;
|
|
wp->yrepeat = 32;
|
|
wp->clipdist = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(wu->Flags2, SPR2_BLUR_TAPER_FAST);
|
|
|
|
wu->floor_dist = Z(1);
|
|
wu->ceiling_dist = Z(1);
|
|
wu->Dist = 200;
|
|
|
|
oclipdist = sp->clipdist;
|
|
sp->clipdist = 1;
|
|
|
|
if (mp[i].dist_over != 0)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang + mp[i].ang);
|
|
HelpMissileLateral(w, mp[i].dist_over);
|
|
wp->ang = NORM_ANGLE(wp->ang - mp[i].ang);
|
|
}
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
MissileSetPos(w, DoNapalm, mp[i].dist_out);
|
|
|
|
sp->clipdist = oclipdist;
|
|
|
|
u->Counter = 0;
|
|
|
|
}
|
|
ang += 512;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSumoSkull(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, np;
|
|
USERp u = User[SpriteNum], nu;
|
|
short New;
|
|
|
|
extern STATE s_SkullExplode[];
|
|
extern STATE s_SkullWait[5][1];
|
|
extern STATEp sg_SkullWait[];
|
|
extern ATTRIBUTE SkullAttrib;
|
|
|
|
|
|
PlaySound(DIGI_SERPSUMMONHEADS, sp, v3df_none);
|
|
|
|
New = SpawnSprite(STAT_ENEMY, SKULL_R0, &s_SkullWait[0][0], sp->sectnum, sp->x, sp->y, SPRITEp_MID(sp), sp->ang, 0);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->xvel = 500;
|
|
SetOwner(SpriteNum, New);
|
|
np->shade = -20;
|
|
np->xrepeat = 64;
|
|
np->yrepeat = 64;
|
|
np->pal = 0;
|
|
|
|
// randomize the head turning angle
|
|
np->ang = RANDOM_P2(2048<<5)>>5;
|
|
|
|
// control direction of spinning
|
|
FLIP(u->Flags, SPR_BOUNCE);
|
|
SET(nu->Flags, TEST(u->Flags, SPR_BOUNCE));
|
|
|
|
nu->StateEnd = s_SkullExplode;
|
|
nu->Rot = sg_SkullWait;
|
|
|
|
nu->Attrib = &SkullAttrib;
|
|
DoActorSetSpeed(SpriteNum, NORM_SPEED);
|
|
nu->Counter = RANDOM_P2(2048);
|
|
nu->sz = np->z;
|
|
nu->Health = 100;
|
|
|
|
// defaults do change the statnum
|
|
EnemyDefaults(New, NULL, NULL);
|
|
//change_sprite_stat(New, STAT_SKIP4);
|
|
SET(np->extra, SPRX_PLAYER_OR_ENEMY);
|
|
|
|
np->clipdist = (128+64) >> 2;
|
|
SET(nu->Flags, SPR_XFLIP_TOGGLE);
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
nu->Radius = 400;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSumoStompAttack(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = &sprite[SpriteNum],tsp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist;
|
|
short reach;
|
|
|
|
|
|
PlaySound(DIGI_30MMEXPLODE, sp, v3df_dontpan|v3df_doppler);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
tsp = &sprite[i];
|
|
|
|
if (tsp != u->tgt_sp)
|
|
break;
|
|
|
|
if (!TEST(tsp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
dist = Distance(sp->x, sp->y, tsp->x, tsp->y);
|
|
|
|
reach = 16384;
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(tsp, sp, reach))
|
|
{
|
|
if (FAFcansee(tsp->x,tsp->y,SPRITEp_MID(tsp),tsp->sectnum,sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum))
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitMiniSumoClap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
int dist;
|
|
short reach;
|
|
|
|
|
|
if (!u->tgt_sp) return 0;
|
|
|
|
dist = Distance(sp->x, sp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
reach = 10000;
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(u->tgt_sp, sp, 1000))
|
|
{
|
|
if (SpriteOverlapZ(SpriteNum, u->tgt_sp - sprite, Z(20)))
|
|
{
|
|
if (FAFcansee(u->tgt_sp->x,u->tgt_sp->y,SPRITEp_MID(u->tgt_sp),u->tgt_sp->sectnum,sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum))
|
|
{
|
|
PlaySound(DIGI_CGTHIGHBONE, sp, v3df_follow|v3df_dontpan);
|
|
DoDamage(u->tgt_sp - sprite, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
else if (dist < CLOSE_RANGE_DIST_FUDGE(u->tgt_sp, sp, reach))
|
|
{
|
|
if (FAFcansee(u->tgt_sp->x,u->tgt_sp->y,SPRITEp_MID(u->tgt_sp),u->tgt_sp->sectnum,sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum))
|
|
{
|
|
PlaySound(DIGI_30MMEXPLODE, sp, v3df_none);
|
|
SpawnFireballFlames(SpriteNum, u->tgt_sp - sprite);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
WeaponAutoAim(SPRITEp sp, short Missile, short ang, SWBOOL test)
|
|
{
|
|
USERp wu = User[Missile];
|
|
USERp u = User[sp - sprite];
|
|
SPRITEp wp = &sprite[Missile];
|
|
short hit_sprite = -1;
|
|
int dist;
|
|
int zh;
|
|
|
|
//return(-1);
|
|
|
|
#if 0
|
|
//formula for leading a player
|
|
dist = Distance(sp->x, sp->y, hp->x, hp->y);
|
|
time_to_target = dist/wp->xvel;
|
|
lead_dist = time_to_target*hp->vel;
|
|
#endif
|
|
|
|
if (u && u->PlayerP)
|
|
{
|
|
if (!TEST(u->PlayerP->Flags, PF_AUTO_AIM))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if ((hit_sprite = DoPickTarget(sp, ang, test)) != -1)
|
|
{
|
|
SPRITEp hp = &sprite[hit_sprite];
|
|
USERp hu = User[hit_sprite];
|
|
|
|
wu->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
|
|
wp->ang = NORM_ANGLE(getangle(hp->x - wp->x, hp->y - wp->y));
|
|
//dist = FindDistance2D(wp->x, wp->y, hp->x, hp->y);
|
|
dist = FindDistance2D(wp->x - hp->x, wp->y - hp->y);
|
|
|
|
if (dist != 0)
|
|
{
|
|
#if 1
|
|
int tos, diff, siz;
|
|
|
|
tos = SPRITEp_TOS(hp);
|
|
diff = wp->z - tos;
|
|
siz = SPRITEp_SIZE_Z(hp);
|
|
|
|
// hit_sprite is below
|
|
if (diff < -Z(50))
|
|
zh = tos + DIV2(siz);
|
|
else
|
|
// hit_sprite is above
|
|
if (diff > Z(50))
|
|
zh = tos + DIV8(siz);
|
|
else
|
|
zh = tos + DIV4(siz);
|
|
|
|
wp->zvel = (wp->xvel * (zh - wp->z)) / dist;
|
|
#else
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
wp->zvel = (wp->xvel * (zh - wp->z)) / dist;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return hit_sprite;
|
|
}
|
|
|
|
int
|
|
WeaponAutoAimZvel(SPRITEp sp, short Missile, int *zvel, short ang, SWBOOL test)
|
|
{
|
|
USERp wu = User[Missile];
|
|
USERp u = User[sp - sprite];
|
|
SPRITEp wp = &sprite[Missile];
|
|
short hit_sprite = -1;
|
|
int dist;
|
|
int zh;
|
|
|
|
//return(-1);
|
|
|
|
#if 0
|
|
//formula for leading a player
|
|
dist = Distance(sp->x, sp->y, hp->x, hp->y);
|
|
time_to_target = dist/wp->xvel;
|
|
lead_dist = time_to_target*hp->vel;
|
|
#endif
|
|
|
|
if (u && u->PlayerP)
|
|
{
|
|
if (!TEST(u->PlayerP->Flags, PF_AUTO_AIM))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if ((hit_sprite = DoPickTarget(sp, ang, test)) != -1)
|
|
{
|
|
SPRITEp hp = &sprite[hit_sprite];
|
|
USERp hu = User[hit_sprite];
|
|
|
|
wu->WpnGoal = hit_sprite;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
|
|
wp->ang = NORM_ANGLE(getangle(hp->x - wp->x, hp->y - wp->y));
|
|
//dist = FindDistance2D(wp->x, wp->y, hp->x, hp->y);
|
|
dist = FindDistance2D(wp->x - hp->x, wp->y - hp->y);
|
|
|
|
if (dist != 0)
|
|
{
|
|
#if 1
|
|
int tos, diff, siz;
|
|
|
|
tos = SPRITEp_TOS(hp);
|
|
diff = wp->z - tos;
|
|
siz = SPRITEp_SIZE_Z(hp);
|
|
|
|
// hit_sprite is below
|
|
if (diff < -Z(50))
|
|
zh = tos + DIV2(siz);
|
|
else
|
|
// hit_sprite is above
|
|
if (diff > Z(50))
|
|
zh = tos + DIV8(siz);
|
|
else
|
|
zh = tos + DIV4(siz);
|
|
|
|
*zvel = (wp->xvel * (zh - wp->z)) / dist;
|
|
#else
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
wp->zvel = (wp->xvel * (zh - wp->z)) / dist;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return hit_sprite;
|
|
}
|
|
|
|
|
|
int
|
|
AimHitscanToTarget(SPRITEp sp, int *z, short *ang, int z_ratio)
|
|
{
|
|
USERp u = User[sp - sprite];
|
|
short hit_sprite = -1;
|
|
int dist;
|
|
int zh;
|
|
int xvect;
|
|
int yvect;
|
|
SPRITEp hp;
|
|
USERp hu;
|
|
|
|
if (!u->tgt_sp)
|
|
return -1;
|
|
|
|
hit_sprite = u->tgt_sp - sprite;
|
|
hp = &sprite[hit_sprite];
|
|
hu = User[hit_sprite];
|
|
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
|
|
// Global set by DoPickTarget
|
|
*ang = getangle(hp->x - sp->x, hp->y - sp->y);
|
|
|
|
// find the distance to the target
|
|
dist = ksqrt(SQ(sp->x - hp->x) + SQ(sp->y - hp->y));
|
|
|
|
if (dist != 0)
|
|
{
|
|
zh = SPRITEp_UPPER(hp);
|
|
|
|
xvect = sintable[NORM_ANGLE(*ang + 512)];
|
|
yvect = sintable[NORM_ANGLE(*ang)];
|
|
|
|
if (hp->x - sp->x != 0)
|
|
//*z = xvect * ((zh - *z)/(hp->x - sp->x));
|
|
*z = scale(xvect,zh - *z,hp->x - sp->x);
|
|
else if (hp->y - sp->y != 0)
|
|
//*z = yvect * ((zh - *z)/(hp->y - sp->y));
|
|
*z = scale(yvect,zh - *z,hp->y - sp->y);
|
|
else
|
|
*z = 0;
|
|
|
|
// so actors won't shoot straight up at you
|
|
// need to be a bit of a distance away
|
|
// before they have a valid shot
|
|
if (labs(*z / dist) > z_ratio)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
return hit_sprite;
|
|
}
|
|
|
|
int
|
|
WeaponAutoAimHitscan(SPRITEp sp, int *z, short *ang, SWBOOL test)
|
|
{
|
|
USERp u = User[sp - sprite];
|
|
short hit_sprite = -1;
|
|
int dist;
|
|
int zh;
|
|
int xvect;
|
|
int yvect;
|
|
|
|
if (u && u->PlayerP)
|
|
{
|
|
if (!TEST(u->PlayerP->Flags, PF_AUTO_AIM))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if ((hit_sprite = DoPickTarget(sp, *ang, test)) != -1)
|
|
{
|
|
SPRITEp hp = &sprite[hit_sprite];
|
|
USERp hu = User[hit_sprite];
|
|
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
|
|
// Global set by DoPickTarget
|
|
//*ang = target_ang;
|
|
*ang = NORM_ANGLE(getangle(hp->x - sp->x, hp->y - sp->y));
|
|
|
|
// find the distance to the target
|
|
dist = ksqrt(SQ(sp->x - hp->x) + SQ(sp->y - hp->y));
|
|
|
|
if (dist != 0)
|
|
{
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
|
|
xvect = sintable[NORM_ANGLE(*ang + 512)];
|
|
yvect = sintable[NORM_ANGLE(*ang)];
|
|
|
|
if (hp->x - sp->x != 0)
|
|
//*z = xvect * ((zh - *z)/(hp->x - sp->x));
|
|
*z = scale(xvect,zh - *z,hp->x - sp->x);
|
|
else if (hp->y - sp->y != 0)
|
|
//*z = yvect * ((zh - *z)/(hp->y - sp->y));
|
|
*z = scale(yvect,zh - *z,hp->y - sp->y);
|
|
else
|
|
*z = 0;
|
|
}
|
|
}
|
|
|
|
return hit_sprite;
|
|
}
|
|
|
|
void
|
|
WeaponHitscanShootFeet(SPRITEp sp, SPRITEp hp, int *zvect)
|
|
{
|
|
int dist;
|
|
int zh;
|
|
int xvect;
|
|
int yvect;
|
|
int z;
|
|
short ang;
|
|
|
|
// Global set by DoPickTarget
|
|
//*ang = target_ang;
|
|
ang = NORM_ANGLE(getangle(hp->x - sp->x, hp->y - sp->y));
|
|
|
|
// find the distance to the target
|
|
dist = ksqrt(SQ(sp->x - hp->x) + SQ(sp->y - hp->y));
|
|
|
|
if (dist != 0)
|
|
{
|
|
zh = SPRITEp_BOS(hp) + Z(20);
|
|
z = sp->z;
|
|
|
|
xvect = sintable[NORM_ANGLE(ang + 512)];
|
|
yvect = sintable[NORM_ANGLE(ang)];
|
|
|
|
if (hp->x - sp->x != 0)
|
|
//*z = xvect * ((zh - *z)/(hp->x - sp->x));
|
|
*zvect = scale(xvect,zh - z, hp->x - sp->x);
|
|
else if (hp->y - sp->y != 0)
|
|
//*z = yvect * ((zh - *z)/(hp->y - sp->y));
|
|
*zvect = scale(yvect,zh - z, hp->y - sp->y);
|
|
else
|
|
*zvect = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
InitStar(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
int zvel;
|
|
|
|
static short dang[] = {-12, 12};
|
|
// static short dang[] = {-0, 0};
|
|
unsigned char i;
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short nw;
|
|
#define STAR_REPEAT 26
|
|
#define STAR_HORIZ_ADJ 100L
|
|
//#define STAR_HORIZ_ADJ 0
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -3);
|
|
|
|
PlaySound(DIGI_STAR, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, STAR1, s_Star, pp->cursectnum, nx, ny, nz, FixedToInt(pp->q16ang), STAR_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
// Attach sound to moving shuriken (* Screw off, yucky sound! *)
|
|
// PlaySound(DIGI_STARWIZ, &wp->x, &wp->y, &wp->z, v3df_follow);
|
|
// Set3DSoundOwner(w);
|
|
|
|
//SET(wp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = wp->xrepeat = STAR_REPEAT;
|
|
wp->shade = -25;
|
|
wp->clipdist = 32L >> 2;
|
|
// wp->zvel was overflowing with this calculation - had to move to a local
|
|
// long var
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (HORIZ_MULT+STAR_HORIZ_ADJ));
|
|
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 100;
|
|
wu->Counter = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
//SET(wu->Flags2, SPR2_BLUR_TAPER_SLOW);
|
|
|
|
// zvel had to be tweaked alot for this weapon
|
|
// MissileSetPos seemed to be pushing the sprite too far up or down when
|
|
// the horizon was tilted. Never figured out why.
|
|
wp->zvel = zvel >> 1;
|
|
if (MissileSetPos(w, DoStar, 1000))
|
|
{
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) != -1)
|
|
{
|
|
zvel = wp->zvel;
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
wu->ox = wp->x;
|
|
wu->oy = wp->y;
|
|
wu->oz = wp->z;
|
|
|
|
for (i = 0; i < (int)SIZ(dang); i++)
|
|
{
|
|
nw = SpawnSprite(STAT_MISSILE, STAR1, s_Star, pp->cursectnum, nx, ny, nz, NORM_ANGLE(wp->ang + dang[i]), wp->xvel);
|
|
np = &sprite[nw];
|
|
nu = User[nw];
|
|
|
|
SetOwner(wp->owner, nw);
|
|
np->yrepeat = np->xrepeat = STAR_REPEAT;
|
|
np->shade = wp->shade;
|
|
|
|
np->extra = wp->extra;
|
|
np->clipdist = wp->clipdist;
|
|
nu->WeaponNum = wu->WeaponNum;
|
|
nu->Radius = wu->Radius;
|
|
nu->ceiling_dist = wu->ceiling_dist;
|
|
nu->floor_dist = wu->floor_dist;
|
|
nu->Flags2 = wu->Flags2;
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(np))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (HORIZ_MULT+STAR_HORIZ_ADJ));
|
|
np->zvel = zvel >> 1;
|
|
|
|
if (MissileSetPos(nw, DoStar, 1000))
|
|
{
|
|
KillSprite(nw);
|
|
return 0;
|
|
}
|
|
|
|
// move the same as middle star
|
|
zvel = wu->zchange;
|
|
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
nu->zchange = zvel;
|
|
|
|
nu->ox = np->x;
|
|
nu->oy = np->y;
|
|
nu->oz = np->z;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if WORM == 1
|
|
void
|
|
InitHeartAttack(PLAYERp pp)
|
|
{
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
USERp u;
|
|
short i = 0;
|
|
short oclipdist;
|
|
|
|
typedef struct
|
|
{
|
|
int dist_over, dist_out;
|
|
short ang;
|
|
} MISSILE_PLACEMENT;
|
|
|
|
static MISSILE_PLACEMENT mp[] =
|
|
{
|
|
{0, 1100, 0},
|
|
};
|
|
|
|
PlayerUpdateAmmo(pp, WPN_HEART, -1);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return;
|
|
|
|
SpriteNum = SpawnSprite(STAT_MISSILE_SKIP4, BLOOD_WORM, s_BloodWorm, pp->cursectnum,
|
|
pp->posx, pp->posy, pp->posz + Z(12), FixedToInt(pp->q16ang), BLOOD_WORM_VELOCITY*2);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
u = User[SpriteNum];
|
|
|
|
sp->hitag = LUMINOUS; //Always full brightness
|
|
|
|
//sp->owner = pp->SpriteP - sprite;
|
|
SetOwner(pp->SpriteP - sprite, SpriteNum);
|
|
sp->shade = -10;
|
|
sp->xrepeat = 52;
|
|
sp->yrepeat = 52;
|
|
sp->clipdist = 0;
|
|
sp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(u->Flags2, SPR2_DONT_TARGET_OWNER);
|
|
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
u->floor_dist = Z(1);
|
|
u->ceiling_dist = Z(1);
|
|
u->Dist = 200;
|
|
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 1;
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
u->zchange = sp->zvel;
|
|
|
|
#if 0
|
|
if (MissileSetPos(SpriteNum, DoBloodWorm, mp[i].dist_out))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(SpriteNum);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
MissileSetPos(SpriteNum, DoBloodWorm, mp[i].dist_out);
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
u->Counter = 0;
|
|
u->Counter2 = 0;
|
|
u->Counter3 = 0;
|
|
u->WaitTics = 0;
|
|
}
|
|
#endif
|
|
|
|
#if WORM == 2
|
|
void
|
|
InitHeartAttack(PLAYERp pp)
|
|
{
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
USERp u;
|
|
short i = 0;
|
|
short oclipdist;
|
|
USERp pu = User[pp->PlayerSprite];
|
|
|
|
typedef struct
|
|
{
|
|
int dist_over, dist_out;
|
|
short ang;
|
|
} MISSILE_PLACEMENT;
|
|
|
|
static MISSILE_PLACEMENT mp[] =
|
|
{
|
|
{0, 1100, 0},
|
|
};
|
|
|
|
PlayerUpdateAmmo(pp, WPN_HEART, -1);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return;
|
|
|
|
SpriteNum = SpawnSprite(STAT_MISSILE_SKIP4, BLOOD_WORM, s_BloodWorm, pp->cursectnum,
|
|
pp->posx, pp->posy, pp->posz + Z(12), FixedToInt(pp->q16ang), BLOOD_WORM_VELOCITY*2);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
u = User[SpriteNum];
|
|
|
|
sp->hitag = LUMINOUS; //Always full brightness
|
|
|
|
//sp->owner = pp->SpriteP - sprite;
|
|
SetOwner(pp->SpriteP - sprite, SpriteNum);
|
|
sp->shade = -10;
|
|
sp->xrepeat = 52;
|
|
sp->yrepeat = 52;
|
|
sp->clipdist = (256) >> 2; // same size a zombie
|
|
sp->zvel = 0;
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(u->Flags2, SPR2_DONT_TARGET_OWNER);
|
|
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
u->floor_dist = Z(1);
|
|
u->ceiling_dist = Z(1);
|
|
u->Dist = 200;
|
|
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 1;
|
|
|
|
u->xchange = MOVEx(sp->xvel, sp->ang);
|
|
u->ychange = MOVEy(sp->xvel, sp->ang);
|
|
u->zchange = sp->zvel;
|
|
|
|
MissileSetPos(SpriteNum, DoBloodWorm, 1100);
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
u->Counter = 0;
|
|
u->Counter2 = 0;
|
|
u->Counter3 = 0;
|
|
u->WaitTics = 0;
|
|
}
|
|
#endif
|
|
|
|
int ContinueHitscan(PLAYERp pp, short sectnum, int x, int y, int z, short ang, int xvect, int yvect, int zvect)
|
|
{
|
|
short j;
|
|
hitdata_t hitinfo;
|
|
USERp u = User[pp->PlayerSprite];
|
|
|
|
FAFhitscan(x, y, z, sectnum,
|
|
xvect, yvect, zvect,
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
return 0;
|
|
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
if (labs(hitinfo.pos.z - sector[hitinfo.sect].ceilingz) <= Z(1))
|
|
{
|
|
hitinfo.pos.z += Z(16);
|
|
if (TEST(sector[hitinfo.sect].ceilingstat, CEILING_STAT_PLAX))
|
|
return 0;
|
|
}
|
|
else if (labs(hitinfo.pos.z - sector[hitinfo.sect].floorz) <= Z(1))
|
|
{
|
|
}
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang, u->ID);
|
|
return 0;
|
|
}
|
|
|
|
QueueHole(hitinfo.sect,hitinfo.wall,hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z);
|
|
}
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
SPRITEp hsp = &sprite[hitinfo.sprite];
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_SHOTGUN, hitinfo.sprite))
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
return 0;
|
|
}
|
|
|
|
if (BulletHitSprite(pp->SpriteP, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, 0))
|
|
return 0;
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
}
|
|
|
|
j = SpawnShotgunSparks(pp, hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang);
|
|
DoHitscanDamage(j, hitinfo.sprite);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitShotgun(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
short daang,ndaang, i, j;
|
|
hitdata_t hitinfo;
|
|
short nsect;
|
|
int daz, ndaz;
|
|
int nx,ny,nz;
|
|
int xvect,yvect,zvect;
|
|
short cstat = 0;
|
|
SPRITEp sp;
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
PlaySound(DIGI_RIOTFIRE2, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (pp->WpnShotgunAuto)
|
|
{
|
|
switch (pp->WpnShotgunType)
|
|
{
|
|
case 1:
|
|
pp->WpnShotgunAuto--;
|
|
}
|
|
}
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
daz = nz = pp->posz + pp->bob_z;
|
|
nsect = pp->cursectnum;
|
|
sp = pp->SpriteP;
|
|
|
|
daang = 64;
|
|
if (WeaponAutoAimHitscan(sp, &daz, &daang, FALSE) != -1)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
daz = (100 - FixedToInt(pp->q16horiz)) * 2000;
|
|
daang = FixedToInt(pp->q16ang);
|
|
}
|
|
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
if (pp->WpnShotgunType == 0)
|
|
{
|
|
ndaz = daz + (RANDOM_RANGE(Z(120)) - Z(45));
|
|
ndaang = NORM_ANGLE(daang + (RANDOM_RANGE(30) - 15));
|
|
}
|
|
else
|
|
{
|
|
ndaz = daz + (RANDOM_RANGE(Z(200)) - Z(65));
|
|
ndaang = NORM_ANGLE(daang + (RANDOM_RANGE(70) - 30));
|
|
}
|
|
|
|
xvect = sintable[NORM_ANGLE(ndaang + 512)];
|
|
yvect = sintable[NORM_ANGLE(ndaang)];
|
|
zvect = ndaz;
|
|
FAFhitscan(nx, ny, nz, nsect, // Start position
|
|
xvect, yvect, zvect,
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
{
|
|
////DSPRINTF(ds,"PROBLEM! - FAFhitscan returned a bad hitinfo.sect");
|
|
//MONO_PRINT(ds);
|
|
continue;
|
|
}
|
|
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
if (labs(hitinfo.pos.z - sector[hitinfo.sect].ceilingz) <= Z(1))
|
|
{
|
|
hitinfo.pos.z += Z(16);
|
|
SET(cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
if (TEST(sector[hitinfo.sect].ceilingstat, CEILING_STAT_PLAX))
|
|
continue;
|
|
|
|
if (SectorIsUnderwaterArea(hitinfo.sect))
|
|
{
|
|
WarpToSurface(&hitinfo.sect, &hitinfo.pos.x, &hitinfo.pos.y, &hitinfo.pos.z);
|
|
ContinueHitscan(pp, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ndaang, xvect, yvect, zvect);
|
|
continue;
|
|
}
|
|
}
|
|
else if (labs(hitinfo.pos.z - sector[hitinfo.sect].floorz) <= Z(1))
|
|
{
|
|
if (TEST(sector[hitinfo.sect].extra, SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE)
|
|
{
|
|
SpawnSplashXY(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,hitinfo.sect);
|
|
|
|
if (SectorIsDiveArea(hitinfo.sect))
|
|
{
|
|
WarpToUnderwater(&hitinfo.sect, &hitinfo.pos.x, &hitinfo.pos.y, &hitinfo.pos.z);
|
|
ContinueHitscan(pp, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ndaang, xvect, yvect, zvect);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ndaang, u->ID);
|
|
continue;
|
|
}
|
|
|
|
QueueHole(hitinfo.sect,hitinfo.wall,hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z);
|
|
}
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
SPRITEp hsp = &sprite[hitinfo.sprite];
|
|
USERp hu = User[hitinfo.sprite];
|
|
|
|
if (hu && hu->ID == TRASHCAN) // JBF: added null check
|
|
{
|
|
extern STATE s_TrashCanPain[];
|
|
|
|
PlaySound(DIGI_TRASHLID, sp, v3df_none);
|
|
if (hu->WaitTics <= 0)
|
|
{
|
|
hu->WaitTics = SEC(2);
|
|
ChangeState(hitinfo.sprite,s_TrashCanPain);
|
|
}
|
|
}
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_SHOTGUN, hitinfo.sprite))
|
|
continue;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
continue;
|
|
}
|
|
|
|
if (BulletHitSprite(pp->SpriteP, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, SHOTGUN_SMOKE))
|
|
continue;
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
}
|
|
|
|
j = SpawnShotgunSparks(pp, hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ndaang);
|
|
DoHitscanDamage(j, hitinfo.sprite);
|
|
}
|
|
|
|
DoPlayerBeginRecoil(pp, SHOTGUN_RECOIL_AMT);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitLaser(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
|
|
DoPlayerBeginRecoil(pp, RAIL_RECOIL_AMT);
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
PlaySound(DIGI_RIOTFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, s_Laser, pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), 300);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 52;
|
|
wp->xrepeat = 52;
|
|
wp->shade = -15;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
// the slower the missile travels the less of a zvel it needs
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
wp->zvel /= 4;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning of the fireball.
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 900);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
// the slower the missile travels the less of a zvel it needs
|
|
// move it 1200 dist in increments - works better
|
|
if (MissileSetPos(w, DoLaserStart, 300))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
if (MissileSetPos(w, DoLaserStart, 300))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
if (MissileSetPos(w, DoLaserStart, 300))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
if (MissileSetPos(w, DoLaserStart, 300))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 5);
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
InitRail(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
int zvel;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
DoPlayerBeginRecoil(pp, RAIL_RECOIL_AMT);
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
PlaySound(DIGI_RAILFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
nz = pp->posz + pp->bob_z + Z(11);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), 1200);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 52;
|
|
wp->xrepeat = 52;
|
|
wp->shade = -15;
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (HORIZ_MULT+17));
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rail[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = RAIL_RADIUS;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
wp->clipdist = 32L>>2;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 700);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
if (TestMissileSetPos(w, DoRailStart, 1200, zvel))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 4);
|
|
}
|
|
else
|
|
zvel = wp->zvel; // Let autoaiming set zvel now
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitZillaRail(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
int zvel;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
PlaySound(DIGI_RAILFIRE, sp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
|
|
nz = SPRITEp_TOS(sp);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], sp->sectnum,
|
|
nx, ny, nz, sp->ang, 1200);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 52;
|
|
wp->xrepeat = 52;
|
|
wp->shade = -15;
|
|
zvel = (100 * (HORIZ_MULT+17));
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rail[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = RAIL_RADIUS;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning
|
|
oclipdist = sp->clipdist;
|
|
sp->clipdist = 0;
|
|
wp->clipdist = 32L>>2;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 700);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
if (TestMissileSetPos(w, DoRailStart, 1200, zvel))
|
|
{
|
|
sp->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
sp->clipdist = oclipdist;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAim(sp, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 4);
|
|
}
|
|
else
|
|
zvel = wp->zvel; // Let autoaiming set zvel now
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitRocket(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
int zvel;
|
|
|
|
#if 0
|
|
static short lat_dist[3] =
|
|
{
|
|
//800,1000,600
|
|
600,900,300
|
|
};
|
|
|
|
static short z_off[3] =
|
|
{
|
|
//Z(8), Z(14), Z(14)
|
|
Z(6), Z(12), Z(12)
|
|
};
|
|
#endif
|
|
|
|
DoPlayerBeginRecoil(pp, ROCKET_RECOIL_AMT);
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
auto const WpnRocketHeat = pp->WpnRocketHeat;
|
|
if (WpnRocketHeat)
|
|
{
|
|
switch (pp->WpnRocketType)
|
|
{
|
|
case 1:
|
|
pp->WpnRocketHeat--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PlaySound(DIGI_RIOTFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
//nz = pp->posz + pp->bob_z + Z(12);
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), ROCKET_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = pp->PlayerSprite;
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 90;
|
|
wp->xrepeat = 90;
|
|
wp->shade = -15;
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (HORIZ_MULT+35));
|
|
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rocket[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 2000;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
// Set default palette
|
|
wp->pal = wu->spal = 17; // White
|
|
|
|
if (WpnRocketHeat)
|
|
{
|
|
switch (pp->WpnRocketType)
|
|
{
|
|
case 1:
|
|
SET(wu->Flags, SPR_FIND_PLAYER);
|
|
wp->pal = wu->spal = 20; // Yellow
|
|
break;
|
|
}
|
|
}
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning of the fireball.
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 900);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
// cancel smoke trail
|
|
wu->Counter = 1;
|
|
if (TestMissileSetPos(w, DoRocket, 1200, zvel))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
// inable smoke trail
|
|
wu->Counter = 0;
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 5);
|
|
}
|
|
else
|
|
zvel = wp->zvel; // Let autoaiming set zvel now
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitBunnyRocket(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
int zvel;
|
|
|
|
#if 0
|
|
static short lat_dist[3] =
|
|
{
|
|
//800,1000,600
|
|
600,900,300
|
|
};
|
|
|
|
static short z_off[3] =
|
|
{
|
|
//Z(8), Z(14), Z(14)
|
|
Z(6), Z(12), Z(12)
|
|
};
|
|
#endif
|
|
|
|
DoPlayerBeginRecoil(pp, ROCKET_RECOIL_AMT);
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
auto const WpnRocketHeat = pp->WpnRocketHeat;
|
|
if (WpnRocketHeat)
|
|
{
|
|
switch (pp->WpnRocketType)
|
|
{
|
|
case 1:
|
|
pp->WpnRocketHeat--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PlaySound(DIGI_BUNNYATTACK, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
//nz = pp->posz + pp->bob_z + Z(12);
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R4, &s_BunnyRocket[0][0], pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), ROCKET_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = pp->PlayerSprite;
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 64;
|
|
wp->xrepeat = 64;
|
|
wp->shade = -15;
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (HORIZ_MULT+35));
|
|
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_BunnyRocket[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 2000;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
if (WpnRocketHeat)
|
|
{
|
|
switch (pp->WpnRocketType)
|
|
{
|
|
case 1:
|
|
SET(wu->Flags, SPR_FIND_PLAYER);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning of the fireball.
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 900);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
// cancel smoke trail
|
|
wu->Counter = 1;
|
|
if (TestMissileSetPos(w, DoRocket, 1200, zvel))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
// inable smoke trail
|
|
wu->Counter = 0;
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 5);
|
|
}
|
|
else
|
|
zvel = wp->zvel; // Let autoaiming set zvel now
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
wu->spal = wp->pal = PALETTE_PLAYER1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitNuke(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
int zvel;
|
|
|
|
|
|
// PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
if (pp->WpnRocketNuke > 0)
|
|
pp->WpnRocketNuke = 0; // Bye Bye little nukie.
|
|
else
|
|
return 0;
|
|
|
|
DoPlayerBeginRecoil(pp, ROCKET_RECOIL_AMT*12);
|
|
|
|
|
|
PlaySound(DIGI_RIOTFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
//nz = pp->posz + pp->bob_z + Z(12);
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), 700);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = pp->PlayerSprite;
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 128;
|
|
wp->xrepeat = 128;
|
|
wp->shade = -15;
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (HORIZ_MULT-36));
|
|
wp->clipdist = 64L>>2;
|
|
|
|
// Set to red palette
|
|
wp->pal = wu->spal = 19;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rocket[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = NUKE_RADIUS;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning of the fireball.
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 900);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
// cancel smoke trail
|
|
wu->Counter = 1;
|
|
if (TestMissileSetPos(w, DoRocket, 1200, zvel))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
// inable smoke trail
|
|
wu->Counter = 0;
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 5);
|
|
}
|
|
else
|
|
zvel = wp->zvel; // Let autoaiming set zvel now
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
PlayerDamageSlide(pp, -40, NORM_ANGLE(FixedToInt(pp->q16ang)+1024)); // Recoil slide
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitEnemyNuke(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
int zvel;
|
|
|
|
|
|
PlaySound(DIGI_RIOTFIRE, sp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
//nz = pp->posz + pp->bob_z + Z(12);
|
|
nz = sp->z + Z(40);
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], sp->sectnum,
|
|
nx, ny, nz, sp->ang, 700);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
SetOwner(sp->owner, w);
|
|
else
|
|
SetOwner(SpriteNum, w);
|
|
|
|
wp->yrepeat = 128;
|
|
wp->xrepeat = 128;
|
|
wp->shade = -15;
|
|
zvel = (100 * (HORIZ_MULT-36));
|
|
wp->clipdist = 64L>>2;
|
|
|
|
// Set to red palette
|
|
wp->pal = wu->spal = 19;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rocket[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = NUKE_RADIUS;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 500);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
// cancel smoke trail
|
|
wu->Counter = 1;
|
|
if (TestMissileSetPos(w, DoRocket, 1200, zvel))
|
|
{
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
// enable smoke trail
|
|
wu->Counter = 0;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAim(sp, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 5);
|
|
}
|
|
else
|
|
zvel = wp->zvel; // Let autoaiming set zvel now
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitMicro(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu,hu;
|
|
SPRITEp wp,hp;
|
|
int nx, ny, nz, dist;
|
|
short w;
|
|
short oclipdist;
|
|
short i,ang;
|
|
TARGET_SORTp ts = TargetSort;
|
|
|
|
//DoPlayerBeginRecoil(pp, MICRO_RECOIL_AMT);
|
|
//PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
|
|
#define MAX_MICRO 1
|
|
|
|
DoPickTarget(pp->SpriteP, 256, FALSE);
|
|
|
|
if (TargetSortCount > MAX_MICRO)
|
|
TargetSortCount = MAX_MICRO;
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < MAX_MICRO; i++)
|
|
{
|
|
if (ts < &TargetSort[TargetSortCount] && ts->sprite_num >= 0)
|
|
{
|
|
hp = &sprite[ts->sprite_num];
|
|
hu = User[ts->sprite_num];
|
|
|
|
ang = getangle(hp->x - nx, hp->y - ny);
|
|
|
|
ts++;
|
|
}
|
|
else
|
|
{
|
|
hp = NULL;
|
|
hu = NULL;
|
|
ang = FixedToInt(pp->q16ang);
|
|
}
|
|
|
|
nz = pp->posz + pp->bob_z + Z(14);
|
|
nz += Z(RANDOM_RANGE(20)) - Z(10);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Micro[0][0], pp->cursectnum,
|
|
nx, ny, nz, ang, 1200);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 24;
|
|
wp->xrepeat = 24;
|
|
wp->shade = -15;
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
wp->clipdist = 64L>>2;
|
|
|
|
// randomize zvelocity
|
|
wp->zvel += RANDOM_RANGE(Z(8)) - Z(5);
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Micro[0]);
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(2);
|
|
wu->floor_dist = Z(2);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
wu->WaitTics = 10 + RANDOM_RANGE(40);
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning of the fireball.
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
#define MICRO_LATERAL 5000
|
|
HelpMissileLateral(w, 1000 + (RANDOM_RANGE(MICRO_LATERAL) - DIV2(MICRO_LATERAL)));
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
// cancel smoke trail
|
|
wu->Counter = 1;
|
|
if (MissileSetPos(w, DoMicro, 700))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
continue;
|
|
}
|
|
// inable smoke trail
|
|
wu->Counter = 0;
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
#define MICRO_ANG 400
|
|
|
|
if (hp)
|
|
{
|
|
dist = Distance(wp->x, wp->y, hp->x, hp->y);
|
|
if (dist != 0)
|
|
{
|
|
int zh;
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
wp->zvel = (wp->xvel * (zh - wp->z)) / dist;
|
|
}
|
|
|
|
wu->WpnGoal = ts->sprite_num;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
else
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang + (RANDOM_RANGE(MICRO_ANG) - DIV2(MICRO_ANG)) - 16);
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitRipperSlash(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum], hu;
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
PlaySound(DIGI_RIPPER2ATTACK, sp, v3df_none);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
hu = User[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
if ((unsigned)FindDistance3D(sp->x - hp->x, sp->y - hp->y, sp->z - hp->z) > hu->Radius + u->Radius)
|
|
continue;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 600) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
// PlaySound(PlayerPainVocs[RANDOM_RANGE(MAX_PAIN)], sp, v3df_none);
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitBunnySlash(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
PlaySound(DIGI_BUNNYATTACK, sp, v3df_none);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 600) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitSerpSlash(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
PlaySound(DIGI_SERPSWORDATTACK, sp, v3df_none);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 800) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
// PlaySound(PlayerPainVocs[RANDOM_RANGE(MAX_PAIN)], sp, v3df_none);
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SWBOOL
|
|
WallSpriteInsideSprite(SPRITEp wsp, SPRITEp sp)
|
|
{
|
|
int x1, y1, x2, y2;
|
|
int xoff;
|
|
int dax, day;
|
|
int xsiz, mid_dist;
|
|
|
|
x1 = wsp->x;
|
|
y1 = wsp->y;
|
|
|
|
xoff = (int) TILE_XOFF(wsp->picnum) + (int) wsp->xoffset;
|
|
|
|
if (TEST(wsp->cstat, CSTAT_SPRITE_XFLIP))
|
|
xoff = -xoff;
|
|
|
|
// x delta
|
|
dax = sintable[wsp->ang] * wsp->xrepeat;
|
|
// y delta
|
|
day = sintable[NORM_ANGLE(wsp->ang + 1024 + 512)] * wsp->xrepeat;
|
|
|
|
xsiz = tilesiz[wsp->picnum].x;
|
|
mid_dist = DIV2(xsiz) + xoff;
|
|
|
|
// starting from the center find the first point
|
|
x1 -= mulscale16(dax, mid_dist);
|
|
// starting from the first point find the end point
|
|
x2 = x1 + mulscale16(dax, xsiz);
|
|
|
|
y1 -= mulscale16(day, mid_dist);
|
|
y2 = y1 + mulscale16(day, xsiz);
|
|
|
|
return clipinsideboxline(sp->x, sp->y, x1, y1, x2, y2, ((int) sp->clipdist) << 2);
|
|
}
|
|
|
|
|
|
int
|
|
DoBladeDamage(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
if (!TEST(hp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist > 2000)
|
|
continue;
|
|
|
|
dist = FindDistance3D(sp->x - hp->x, sp->y - hp->y, sp->z - hp->z);
|
|
|
|
if (dist > 2000)
|
|
continue;
|
|
|
|
if (WallSpriteInsideSprite(sp, hp))
|
|
{
|
|
DoDamage(i, SpriteNum);
|
|
// PlaySound(PlayerPainVocs[RANDOM_RANGE(MAX_PAIN)], sp, v3df_none);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoStaticFlamesDamage(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
if (!TEST(hp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist > 2000)
|
|
continue;
|
|
|
|
dist = FindDistance3D(sp->x - hp->x, sp->y - hp->y, sp->z - hp->z);
|
|
|
|
if (dist > 2000)
|
|
continue;
|
|
|
|
if (SpriteOverlap(SpriteNum, i)) // If sprites are overlapping, cansee will fail!
|
|
DoDamage(i, SpriteNum);
|
|
else if (u->Radius > 200)
|
|
{
|
|
if (FAFcansee(sp->x,sp->y,SPRITEp_MID(sp),sp->sectnum,hp->x,hp->y,SPRITEp_MID(hp),hp->sectnum))
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitCoolgBash(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
PlaySound(DIGI_CGTHIGHBONE, sp, v3df_none);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
// don't set off mine
|
|
if (!TEST(hp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
continue;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 600) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
// PlaySound(PlayerPainVocs[RANDOM_RANGE(MAX_PAIN)], sp, v3df_none);
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSkelSlash(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
PlaySound(DIGI_SPBLADE, sp, v3df_none);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 600) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
// PlaySound(PlayerPainVocs[RANDOM_RANGE(MAX_PAIN)], sp, v3df_none);
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitGoroChop(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
PlaySound(DIGI_GRDSWINGAXE, sp, v3df_none);
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
|
|
if (i == SpriteNum)
|
|
break;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 700) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
PlaySound(DIGI_GRDAXEHIT, sp, v3df_none);
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitHornetSting(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
short HitSprite = NORM_SPRITE(u->ret);
|
|
|
|
DoDamage(HitSprite, SpriteNum);
|
|
InitActorReposition(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSerpSpell(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], np;
|
|
USERp u = User[SpriteNum], nu;
|
|
int dist;
|
|
short New, i;
|
|
short oclipdist;
|
|
|
|
static short lat_ang[] =
|
|
{
|
|
512, -512
|
|
};
|
|
|
|
static short delta_ang[] =
|
|
{
|
|
-10, 10
|
|
};
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
sp->ang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
|
|
New = SpawnSprite(STAT_MISSILE, SERP_METEOR, &sg_SerpMeteor[0][0], sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 1500);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
np->z = SPRITEp_TOS(sp);
|
|
|
|
nu->RotNum = 5;
|
|
NewStateGroup(New, &sg_SerpMeteor[0]);
|
|
nu->StateEnd = s_MirvMeteorExp;
|
|
|
|
//np->owner = SpriteNum;
|
|
SetOwner(SpriteNum, New);
|
|
np->shade = -40;
|
|
PlaySound(DIGI_SERPMAGICLAUNCH, sp, v3df_none);
|
|
nu->spal = np->pal = 27; // Bright Green
|
|
np->xrepeat = 64;
|
|
np->yrepeat = 64;
|
|
np->clipdist = 32L >> 2;
|
|
np->zvel = 0;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
nu->ceiling_dist = Z(16);
|
|
nu->floor_dist = Z(16);
|
|
nu->Dist = 200;
|
|
|
|
oclipdist = sp->clipdist;
|
|
sp->clipdist = 1;
|
|
|
|
np->ang = NORM_ANGLE(np->ang + lat_ang[i]);
|
|
HelpMissileLateral(New, 4200L);
|
|
np->ang = NORM_ANGLE(np->ang - lat_ang[i]);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(np->x, np->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
if (dist != 0)
|
|
np->zvel = (np->xvel * (SPRITEp_UPPER(u->tgt_sp) - np->z)) / dist;
|
|
|
|
np->ang = NORM_ANGLE(np->ang + delta_ang[i]);
|
|
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
nu->zchange = np->zvel;
|
|
|
|
MissileSetPos(New, DoMirvMissile, 400);
|
|
sp->clipdist = oclipdist;
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnDemonFist(int16_t Weapon)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
SPRITEp exp;
|
|
USERp eu;
|
|
short explosion;
|
|
|
|
ASSERT(u);
|
|
if (TEST(u->Flags, SPR_SUICIDE))
|
|
return -1;
|
|
|
|
//PlaySound(DIGI_ITEM_SPAWN, sp, v3df_none);
|
|
explosion = SpawnSprite(STAT_MISSILE, 0, s_TeleportEffect, sp->sectnum,
|
|
sp->x, sp->y, SPRITEp_MID(sp), sp->ang, 0);
|
|
|
|
exp = &sprite[explosion];
|
|
eu = User[explosion];
|
|
|
|
exp->hitag = LUMINOUS; //Always full brightness
|
|
exp->owner = -1;
|
|
exp->shade = -40;
|
|
exp->xrepeat = 32;
|
|
exp->yrepeat = 32;
|
|
eu->spal = exp->pal = 25;
|
|
|
|
SET(exp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(exp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
eu->Radius = DamageData[DMG_BASIC_EXP].radius;
|
|
|
|
if (RANDOM_P2(1024<<8)>>8 > 600)
|
|
SET(exp->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024<<8)>>8 > 600)
|
|
SET(exp->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
return explosion;
|
|
}
|
|
|
|
int
|
|
InitSerpMonstSpell(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], np;
|
|
USERp u = User[SpriteNum], nu;
|
|
int dist;
|
|
short New, i;
|
|
short oclipdist;
|
|
|
|
static short lat_ang[] =
|
|
{
|
|
512, -512
|
|
};
|
|
|
|
static short delta_ang[] =
|
|
{
|
|
-10, 10
|
|
};
|
|
|
|
PlaySound(DIGI_MISSLFIRE, sp, v3df_none);
|
|
|
|
for (i = 0; i < 1; i++)
|
|
{
|
|
sp->ang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
|
|
New = SpawnSprite(STAT_MISSILE, SERP_METEOR, &sg_SerpMeteor[0][0], sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 500);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
nu->spal = np->pal = 25; // Bright Red
|
|
np->z = SPRITEp_TOS(sp);
|
|
|
|
nu->RotNum = 5;
|
|
NewStateGroup(New, &sg_SerpMeteor[0]);
|
|
//nu->StateEnd = s_MirvMeteorExp;
|
|
nu->StateEnd = s_TeleportEffect2;
|
|
|
|
SetOwner(SpriteNum, New);
|
|
np->shade = -40;
|
|
np->xrepeat = 122;
|
|
np->yrepeat = 116;
|
|
np->clipdist = 32L >> 2;
|
|
np->zvel = 0;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
nu->ceiling_dist = Z(16);
|
|
nu->floor_dist = Z(16);
|
|
|
|
nu->Dist = 200;
|
|
|
|
oclipdist = sp->clipdist;
|
|
sp->clipdist = 1;
|
|
|
|
np->ang = NORM_ANGLE(np->ang + lat_ang[i]);
|
|
HelpMissileLateral(New, 4200L);
|
|
np->ang = NORM_ANGLE(np->ang - lat_ang[i]);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(np->x, np->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
if (dist != 0)
|
|
np->zvel = (np->xvel * (SPRITEp_UPPER(u->tgt_sp) - np->z)) / dist;
|
|
|
|
np->ang = NORM_ANGLE(np->ang + delta_ang[i]);
|
|
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
nu->zchange = np->zvel;
|
|
|
|
MissileSetPos(New, DoMirvMissile, 400);
|
|
sp->clipdist = oclipdist;
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(nu->Flags, SPR_UNDERWATER);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoTeleRipper(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
extern void Ripper2Hatch(short Weapon);
|
|
|
|
PlaySound(DIGI_ITEM_SPAWN, sp, v3df_none);
|
|
Ripper2Hatch(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitEnemyRocket(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
|
|
PlaySound(DIGI_NINJARIOTATTACK, sp, v3df_none);
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - DIV2(SPRITEp_SIZE_Z(sp))-Z(8);
|
|
|
|
// Spawn a shot
|
|
// wp = &sprite[w = SpawnSprite(STAT_MISSILE, STAR1, s_Star, sp->sectnum,
|
|
// nx, ny, nz, u->tgt_sp->ang, 250)];
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R2, &s_Rocket[0][0], sp->sectnum,
|
|
nx, ny, nz-Z(8), u->tgt_sp->ang, NINJA_BOLT_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
// Set default palette
|
|
wp->pal = wu->spal = 17; // White
|
|
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
SetOwner(sp->owner, w);
|
|
else
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 28;
|
|
wp->xrepeat = 28;
|
|
wp->shade = -15;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rocket[0]);
|
|
wu->Radius = 200;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (u->spal == PAL_XLAT_LT_TAN)
|
|
{
|
|
SET(wu->Flags, SPR_FIND_PLAYER);
|
|
wp->pal = wu->spal = 20; // Yellow
|
|
}
|
|
|
|
MissileSetPos(w, DoBoltThinMan, 400);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
return w;
|
|
}
|
|
|
|
int
|
|
InitEnemyRail(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
short pnum=0;
|
|
// short oclipdist;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
// if co-op don't hurt teammate
|
|
if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE && u->ID == ZOMBIE_RUN_R0)
|
|
{
|
|
PLAYERp pp;
|
|
SPRITEp psp;
|
|
|
|
// Check all players
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
pp = &Player[pnum];
|
|
psp = &sprite[pp->PlayerSprite];
|
|
|
|
if (u->tgt_sp == psp)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
PlaySound(DIGI_RAILFIRE, sp, v3df_dontpan|v3df_doppler);
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
|
|
// add a bit of randomness
|
|
if (RANDOM_P2(1024) < 512)
|
|
sp->ang = NORM_ANGLE(sp->ang + RANDOM_P2(128) - 64);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - DIV2(SPRITEp_SIZE_Z(sp))-Z(8);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], sp->sectnum,
|
|
nx, ny, nz, sp->ang, 1200);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
SetOwner(sp->owner, w);
|
|
else
|
|
SetOwner(SpriteNum, w);
|
|
|
|
wp->yrepeat = 52;
|
|
wp->xrepeat = 52;
|
|
wp->shade = -15;
|
|
wp->zvel = 0;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rail[0]);
|
|
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wu->Flags2, SPR2_SO_MISSILE);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (TestMissileSetPos(w, DoRailStart, 600, wp->zvel))
|
|
{
|
|
//sprite->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
int
|
|
InitZillaRocket(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w, i;
|
|
|
|
typedef struct
|
|
{
|
|
int dist_over, dist_out;
|
|
short ang;
|
|
} MISSILE_PLACEMENT;
|
|
|
|
static MISSILE_PLACEMENT mp[] =
|
|
{
|
|
{600 * 6, 400, 512},
|
|
{900 * 6, 400, 512},
|
|
{1100 * 6, 400, 512},
|
|
{600 * 6, 400, -512},
|
|
{900 * 6, 400, -512},
|
|
{1100 * 6, 400, -512},
|
|
};
|
|
|
|
PlaySound(DIGI_NINJARIOTATTACK, sp, v3df_none);
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y);
|
|
|
|
for (i = 0; i < (int)SIZ(mp); i++)
|
|
{
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - DIV2(SPRITEp_SIZE_Z(sp))-Z(8);
|
|
|
|
// Spawn a shot
|
|
// wp = &sprite[w = SpawnSprite(STAT_MISSILE, STAR1, s_Star, sp->sectnum,
|
|
// nx, ny, nz, u->tgt_sp->ang, 250)];
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R2, &s_Rocket[0][0], sp->sectnum,
|
|
nx, ny, nz-Z(8), u->tgt_sp->ang, NINJA_BOLT_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 28;
|
|
wp->xrepeat = 28;
|
|
wp->shade = -15;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rocket[0]);
|
|
wu->Radius = 200;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
// Zilla has seekers!
|
|
if (i != 1 && i != 4)
|
|
wp->pal = wu->spal = 17; // White
|
|
else
|
|
{
|
|
SET(wu->Flags, SPR_FIND_PLAYER);
|
|
wp->pal = wu->spal = 20; // Yellow
|
|
}
|
|
|
|
if (mp[i].dist_over != 0)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang + mp[i].ang);
|
|
HelpMissileLateral(w, mp[i].dist_over);
|
|
wp->ang = NORM_ANGLE(wp->ang - mp[i].ang);
|
|
}
|
|
|
|
MissileSetPos(w, DoBoltThinMan, mp[i].dist_out);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
int
|
|
InitEnemyStar(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = NORM_ANGLE(getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y));
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = SPRITEp_MID(sp);
|
|
|
|
// Spawn a shot
|
|
wp = &sprite[w = SpawnSprite(STAT_MISSILE, STAR1, s_Star, sp->sectnum,
|
|
nx, ny, nz, u->tgt_sp->ang, NINJA_STAR_VELOCITY)];
|
|
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 16;
|
|
wp->xrepeat = 16;
|
|
wp->shade = -25;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
MissileSetPos(w, DoStar, 400);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
//
|
|
// Star Power Up Code
|
|
//
|
|
|
|
#if 0
|
|
if (sp->pal == PALETTE_PLAYER0)
|
|
{
|
|
static short dang[] = {-28, 28};
|
|
char i;
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short sn;
|
|
|
|
PlaySound(DIGI_STAR, sp, v3df_none);
|
|
for (i = 0; i < SIZ(dang); i++)
|
|
{
|
|
sn = SpawnSprite(STAT_MISSILE, STAR1, s_Star, sp->sectnum, wp->x, wp->y, wp->z, NORM_ANGLE(wp->ang + dang[i]), wp->xvel);
|
|
np = &sprite[sn];
|
|
nu = User[sn];
|
|
|
|
SetOwner(wp->owner, sn);
|
|
np->yrepeat = wp->yrepeat;
|
|
np->xrepeat = wp->xrepeat;
|
|
np->shade = wp->shade;
|
|
nu->WeaponNum = wu->WeaponNum;
|
|
nu->Radius = wu->Radius;
|
|
nu->xchange = wu->xchange;
|
|
nu->ychange = wu->ychange;
|
|
nu->zchange = 0;
|
|
np->zvel = 0;
|
|
|
|
MissileSetPos(sn, DoStar, 400);
|
|
|
|
nu->zchange = wu->zchange;
|
|
np->zvel = wp->zvel;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
|
|
PlaySound(DIGI_STAR, sp, v3df_none);
|
|
|
|
|
|
|
|
return w;
|
|
}
|
|
|
|
int
|
|
InitEnemyCrossbow(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = NORM_ANGLE(getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y));
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = SPRITEp_MID(sp)-Z(14);
|
|
|
|
// Spawn a shot
|
|
wp = &sprite[w = SpawnSprite(STAT_MISSILE, CROSSBOLT, &s_CrossBolt[0][0], sp->sectnum,
|
|
nx, ny, nz, u->tgt_sp->ang, 800)];
|
|
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->xrepeat = 16;
|
|
wp->yrepeat = 26;
|
|
wp->shade = -25;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_CrossBolt[0]);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
MissileSetPos(w, DoStar, 400);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
//
|
|
// Star Power Up Code
|
|
//
|
|
|
|
#if 0
|
|
if (sp->pal == PALETTE_PLAYER0)
|
|
{
|
|
static short dang[] = {-28, 28};
|
|
char i;
|
|
SPRITEp np;
|
|
USERp nu;
|
|
short sn;
|
|
|
|
PlaySound(DIGI_STAR, sp, v3df_none);
|
|
for (i = 0; i < SIZ(dang); i++)
|
|
{
|
|
sn = SpawnSprite(STAT_MISSILE, CROSSBOLT, s_CrossBolt, sp->sectnum, wp->x, wp->y, wp->z, NORM_ANGLE(wp->ang + dang[i]), wp->xvel);
|
|
np = &sprite[sn];
|
|
nu = User[sn];
|
|
|
|
SetOwner(wp->owner, sn);
|
|
np->yrepeat = wp->yrepeat;
|
|
np->xrepeat = wp->xrepeat;
|
|
np->shade = wp->shade;
|
|
nu->WeaponNum = wu->WeaponNum;
|
|
nu->Radius = wu->Radius;
|
|
nu->xchange = wu->xchange;
|
|
nu->ychange = wu->ychange;
|
|
nu->zchange = 0;
|
|
np->zvel = 0;
|
|
|
|
MissileSetPos(sn, DoStar, 400);
|
|
|
|
nu->zchange = wu->zchange;
|
|
np->zvel = wp->zvel;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
|
|
PlaySound(DIGI_STAR, sp, v3df_none);
|
|
|
|
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
int
|
|
InitSkelSpell(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
PlaySound(DIGI_SPELEC, sp, v3df_none);
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = NORM_ANGLE(getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y));
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - DIV2(SPRITEp_SIZE_Z(sp));
|
|
|
|
// Spawn a shot
|
|
w = SpawnSprite(STAT_MISSILE, ELECTRO_ENEMY, s_Electro, sp->sectnum,
|
|
nx, ny, nz, u->tgt_sp->ang, SKEL_ELECTRO_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->xrepeat -= 20;
|
|
wp->yrepeat -= 20;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(nx, ny, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - nz)) / dist;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
MissileSetPos(w, DoElectro, 400);
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
int
|
|
InitCoolgFire(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = NORM_ANGLE(getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y));
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
|
|
nz = sp->z - Z(16);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
PlaySound(DIGI_CGMAGIC, sp, v3df_follow);
|
|
|
|
w = SpawnSprite(STAT_MISSILE, COOLG_FIRE, s_CoolgFire, sp->sectnum,
|
|
nx, ny, nz, u->tgt_sp->ang, COOLG_FIRE_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->hitag = LUMINOUS;
|
|
wp->yrepeat = 18;
|
|
wp->xrepeat = 18;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 32L>>2;
|
|
wu->ceiling_dist = Z(4);
|
|
wu->floor_dist = Z(4);
|
|
if (u->ID == RIPPER_RUN_R0)
|
|
wu->spal = wp->pal = 27; // Bright Green
|
|
else
|
|
wu->spal = wp->pal = 25; // Bright Red
|
|
|
|
PlaySound(DIGI_MAGIC1, wp, v3df_follow|v3df_doppler);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(nx, ny, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
// (velocity * difference between the target and the throwing star) /
|
|
// distance
|
|
wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - nz)) / dist;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
nx = ((int) 728 * (int) sintable[NORM_ANGLE(nang + 512)]) >> 14;
|
|
ny = ((int) 728 * (int) sintable[nang]) >> 14;
|
|
|
|
move_missile(w, nx, ny, 0L, wu->ceiling_dist, wu->floor_dist, 0, 3L);
|
|
|
|
return w;
|
|
}
|
|
|
|
int DoCoolgDrip(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
u->Counter += 220;
|
|
sp->z += u->Counter;
|
|
|
|
if (sp->z > u->loz - u->floor_dist)
|
|
{
|
|
sp->z = u->loz - u->floor_dist;
|
|
sp->yrepeat = sp->xrepeat = 32;
|
|
ChangeState(SpriteNum, s_GoreFloorSplash);
|
|
if (u->spal == PALETTE_BLUE_LIGHTING)
|
|
PlaySound(DIGI_DRIP, sp, v3df_none);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitCoolgDrip(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp wu;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z;
|
|
|
|
w = SpawnSprite(STAT_MISSILE, COOLG_DRIP, s_CoolgDrip, sp->sectnum,
|
|
nx, ny, nz, sp->ang, 0);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = wp->xrepeat = 20;
|
|
wp->shade = -5;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 16L>>2;
|
|
wu->ceiling_dist = Z(4);
|
|
wu->floor_dist = Z(4);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
DoFindGroundPoint(SpriteNum);
|
|
|
|
return w;
|
|
}
|
|
|
|
int
|
|
GenerateDrips(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz;
|
|
short w = 0;
|
|
|
|
if ((u->WaitTics-=ACTORMOVETICS) <= 0)
|
|
{
|
|
if (sp->lotag == 0)
|
|
u->WaitTics = RANDOM_P2(256<<8)>>8;
|
|
else
|
|
u->WaitTics = (sp->lotag * 120) + SEC(RANDOM_RANGE(3<<8)>>8);
|
|
|
|
if (TEST_BOOL2(sp))
|
|
{
|
|
w = SpawnBubble(SpriteNum);
|
|
return w;
|
|
}
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z;
|
|
|
|
w = SpawnSprite(STAT_SHRAP, COOLG_DRIP, s_CoolgDrip, sp->sectnum,
|
|
nx, ny, nz, sp->ang, 0);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = wp->xrepeat = 20;
|
|
wp->shade = -10;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 16L>>2;
|
|
wu->ceiling_dist = Z(4);
|
|
wu->floor_dist = Z(4);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
if (TEST_BOOL1(sp))
|
|
wu->spal = wp->pal = PALETTE_BLUE_LIGHTING;
|
|
|
|
DoFindGroundPoint(SpriteNum);
|
|
}
|
|
return w;
|
|
}
|
|
|
|
int
|
|
InitEelFire(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum], hu;
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
SPRITEp hp;
|
|
short i, nexti;
|
|
unsigned stat;
|
|
int dist, a, b, c;
|
|
|
|
for (stat = 0; stat < SIZ(StatDamageList); stat++)
|
|
{
|
|
TRAVERSE_SPRITE_STAT(headspritestat[StatDamageList[stat]], i, nexti)
|
|
{
|
|
hp = &sprite[i];
|
|
hu = User[i];
|
|
|
|
if (i == SpriteNum)
|
|
continue;
|
|
|
|
if (hp != u->tgt_sp)
|
|
continue;
|
|
|
|
if ((unsigned)FindDistance3D(sp->x - hp->x, sp->y - hp->y, sp->z - hp->z) > hu->Radius + u->Radius)
|
|
continue;
|
|
|
|
DISTANCE(hp->x, hp->y, sp->x, sp->y, dist, a, b, c);
|
|
|
|
if (dist < CLOSE_RANGE_DIST_FUDGE(sp, hp, 600) && FACING_RANGE(hp, sp, 150))
|
|
{
|
|
PlaySound(DIGI_GIBS1, sp, v3df_none);
|
|
DoDamage(i, SpriteNum);
|
|
}
|
|
else
|
|
InitActorReposition(SpriteNum);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitFireballTrap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp wu;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
PlaySound(DIGI_FIREBALL1, sp, v3df_none);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - SPRITEp_SIZE_Z(sp);
|
|
|
|
// Spawn a shot
|
|
w = SpawnSprite(STAT_MISSILE, FIREBALL, s_Fireball, sp->sectnum, nx, ny, nz,
|
|
sp->ang, FIREBALL_TRAP_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
SetOwner(SpriteNum, w);
|
|
wp->xrepeat -= 20;
|
|
wp->yrepeat -= 20;
|
|
wp->shade = -40;
|
|
wp->clipdist = 32>>2;
|
|
wp->zvel = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
wu->WeaponNum = WPN_HOTHEAD;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
return w;
|
|
}
|
|
|
|
int
|
|
InitBoltTrap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp wu;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
PlaySound(DIGI_RIOTFIRE, sp, v3df_none);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - SPRITEp_SIZE_Z(sp);
|
|
|
|
// Spawn a shot
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], sp->sectnum, nx, ny, nz,
|
|
sp->ang, BOLT_TRAP_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 32;
|
|
wp->xrepeat = 32;
|
|
wp->shade = -15;
|
|
wp->zvel = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rocket[0]);
|
|
wu->Radius = 200;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
return w;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
InitEnemyCrossbow(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp u = User[SpriteNum], wu;
|
|
int nx, ny, nz, dist, nang;
|
|
short w;
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = nang = NORM_ANGLE(getangle(u->tgt_sp->x - sp->x, u->tgt_sp->y - sp->y));
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = SPRITEp_MID(sp);
|
|
|
|
// Spawn a shot
|
|
wp = &sprite[w = SpawnSprite(STAT_MISSILE, CROSSBOLT, s_CrossBolt, sp->sectnum,
|
|
nx, ny, nz, u->tgt_sp->ang, 800)];
|
|
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->xrepeat = 16;
|
|
wp->yrepeat = 26;
|
|
wp->shade = -25;
|
|
wp->zvel = 0;
|
|
wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_CrossBolt);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
MissileSetPos(w, DoStar, 400);
|
|
|
|
// find the distance to the target (player)
|
|
dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
PlaySound(DIGI_STAR, sp, v3df_none);
|
|
|
|
return w;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
InitSpearTrap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], wp;
|
|
USERp wu;
|
|
//USERp u = User[SpriteNum];
|
|
int nx, ny, nz;
|
|
short w;
|
|
//short nang;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = SPRITEp_MID(sp);
|
|
|
|
// Spawn a shot
|
|
w = SpawnSprite(STAT_MISSILE, CROSSBOLT, &s_CrossBolt[0][0], sp->sectnum, nx, ny, nz, sp->ang, 750);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->xrepeat = 16;
|
|
wp->yrepeat = 26;
|
|
wp->shade = -25;
|
|
//wp->zvel = 0;
|
|
//wp->ang = nang;
|
|
wp->clipdist = 64L>>2;
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_CrossBolt[0]);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
//MissileSetPos(w, DoStar, 400);
|
|
|
|
// find the distance to the target (player)
|
|
//int dist = Distance(wp->x, wp->y, u->tgt_sp->x, u->tgt_sp->y);
|
|
|
|
//if (dist != 0)
|
|
//wu->zchange = wp->zvel = (wp->xvel * (SPRITEp_UPPER(u->tgt_sp) - wp->z)) / dist;
|
|
|
|
PlaySound(DIGI_STAR, sp, v3df_none);
|
|
return w;
|
|
}
|
|
|
|
int
|
|
DoSuicide(short SpriteNum)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoDefaultStat(short SpriteNum)
|
|
{
|
|
change_sprite_stat(SpriteNum, STAT_DEFAULT);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitTracerUzi(PLAYERp pp)
|
|
{
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
USERp u = User[pp->PlayerSprite];
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
int nx, ny, nz;
|
|
short w;
|
|
int oclipdist;
|
|
|
|
short lat_dist[] = {800,-800};
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
//nz = pp->posz + pp->bob_z + Z(8);
|
|
//nz = pp->posz + pp->bob_z + Z(8) + ((100 - FixedToInt(pp->q16horiz)) * 72);
|
|
nz = pp->posz + Z(8) + ((100 - FixedToInt(pp->q16horiz)) * 72);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, 0, s_Tracer, pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), TRACER_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
//wp->owner = pp->PlayerSprite;
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 10;
|
|
wp->xrepeat = 10;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
//wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 50;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
if (TEST(pp->Flags, PF_TWO_UZI) && pp->WpnUziType == 0)
|
|
HelpMissileLateral(w, lat_dist[RANDOM_P2(2<<8)>>8]);
|
|
else
|
|
HelpMissileLateral(w, lat_dist[0]);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (MissileSetPos(w, DoTracerStart, 800))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * (wp->xvel/8));
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
WeaponAutoAim(pp->SpriteP, w, 32, FALSE);
|
|
|
|
// a bit of randomness
|
|
wp->ang = NORM_ANGLE(wp->ang + RANDOM_RANGE(30) - 15);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitTracerTurret(short SpriteNum, short Operator, int horiz)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z + ((100 - horiz) * 72);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, 0, s_Tracer, sp->sectnum,
|
|
nx, ny, nz, sp->ang, TRACER_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
if (Operator >= 0)
|
|
SetOwner(Operator, w);
|
|
wp->yrepeat = 10;
|
|
wp->xrepeat = 10;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 50;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
wp->zvel = ((100 - horiz) * (wp->xvel/8));
|
|
|
|
WeaponAutoAim(sp, w, 32, FALSE);
|
|
|
|
// a bit of randomness
|
|
wp->ang = NORM_ANGLE(wp->ang + RANDOM_RANGE(30) - 15);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 1
|
|
int
|
|
InitTracerAutoTurret(short SpriteNum, short Operator, int xchange, int ychange, int zchange)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, 0, s_Tracer, sp->sectnum,
|
|
nx, ny, nz, sp->ang, TRACER_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
if (Operator >= 0)
|
|
SetOwner(Operator, w);
|
|
wp->yrepeat = 10;
|
|
wp->xrepeat = 10;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 50;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
wu->xchange = xchange;
|
|
wu->ychange = ychange;
|
|
wu->zchange = zchange;
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
BulletHitSprite(SPRITEp sp, short hit_sprite, int hit_x, int hit_y, int hit_z, short ID)
|
|
{
|
|
vec3_t hit_pos = { hit_x, hit_y, hit_z };
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
USERp hu = User[hit_sprite];
|
|
SPRITEp wp;
|
|
// USERp wu;
|
|
short New;
|
|
short id;
|
|
|
|
// hit a NPC or PC?
|
|
if (TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
// spawn a red splotch
|
|
// !FRANK! this if was incorrect - its not who is HIT, its who is SHOOTING
|
|
//if(!hu->PlayerP)
|
|
if (User[sp - sprite]->PlayerP)
|
|
id = UZI_SMOKE;
|
|
else if (TEST(User[sp - sprite]->Flags, SPR_SO_ATTACHED))
|
|
id = UZI_SMOKE;
|
|
else // Spawn NPC uzi with less damage
|
|
id = UZI_SMOKE+2;
|
|
|
|
if (ID>0) id = ID;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, id, s_UziSmoke, 0, hit_x, hit_y, hit_z, sp->ang, 0);
|
|
wp = &sprite[New];
|
|
// wu = User[New];
|
|
wp->shade = -40;
|
|
|
|
if (hu->PlayerP)
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
switch (hu->ID)
|
|
{
|
|
case TRASHCAN:
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
case 623:
|
|
case ZILLA_RUN_R0:
|
|
wp->xrepeat = UZI_SMOKE_REPEAT;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT;
|
|
if (RANDOM_P2(1024) > 800)
|
|
SpawnShrapX(hit_sprite);
|
|
break;
|
|
default:
|
|
wp->xrepeat = UZI_SMOKE_REPEAT/3;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT/3;
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
//wu->spal = wp->pal = PALETTE_RED_LIGHTING;
|
|
break;
|
|
}
|
|
|
|
SetOwner(sp - sprite, New);
|
|
wp->ang = sp->ang;
|
|
|
|
setspritez(New, &hit_pos);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
if ((RANDOM_P2(1024<<5)>>5) < 512+128)
|
|
{
|
|
if (!hu->PlayerP)
|
|
SpawnBlood(hit_sprite, -1, sp->ang, hit_x, hit_y, hit_z);
|
|
else
|
|
SpawnBlood(hit_sprite, -1, sp->ang, hit_x, hit_y, hit_z+Z(20));
|
|
|
|
// blood comes out the other side?
|
|
if ((RANDOM_P2(1024<<5)>>5) < 256)
|
|
{
|
|
if (!hu->PlayerP)
|
|
SpawnBlood(hit_sprite, -1, NORM_ANGLE(sp->ang+1024),hit_x, hit_y, hit_z);
|
|
if (hu->ID != TRASHCAN && hu->ID != ZILLA_RUN_R0)
|
|
QueueWallBlood(hit_sprite, sp->ang); //QueueWallBlood needs bullet angle.
|
|
}
|
|
}
|
|
|
|
DoHitscanDamage(New, hit_sprite);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int SpawnWallHole(short hit_sect, short hit_wall, int hit_x, int hit_y, int hit_z)
|
|
{
|
|
short w,nw,wall_ang;
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
|
|
SpriteNum = COVERinsertsprite(hit_sect, STAT_DEFAULT);
|
|
sp = &sprite[SpriteNum];
|
|
sp->owner = -1;
|
|
sp->xrepeat = sp->yrepeat = 16;
|
|
sp->cstat = sp->pal = sp->shade = sp->extra = sp->clipdist = sp->xoffset = sp->yoffset = 0;
|
|
sp->x = hit_x;
|
|
sp->y = hit_y;
|
|
sp->z = hit_z;
|
|
sp->picnum = 2151;
|
|
|
|
//SET(sp->cstat, CSTAT_SPRITE_TRANSLUCENT|CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
SET(sp->cstat, CSTAT_SPRITE_ONE_SIDED);
|
|
|
|
w = hit_wall;
|
|
nw = wall[w].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wall[w].x, wall[nw].y - wall[w].y)-512);
|
|
|
|
sp->ang = NORM_ANGLE(wall_ang + 1024);
|
|
|
|
// int nx,ny;
|
|
//nx = (sintable[(512 + FixedToInt(Player[0].q16ang)) & 2047] >> 7);
|
|
//ny = (sintable[FixedToInt(Player[0].q16ang)] >> 7);
|
|
//sp->x -= nx;
|
|
//sp->y -= ny;
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
SWBOOL
|
|
HitscanSpriteAdjust(short SpriteNum, short hit_wall)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
int16_t ang;
|
|
int xvect,yvect;
|
|
short sectnum;
|
|
|
|
#if 1
|
|
if (hit_wall >= 0)
|
|
{
|
|
uint16_t const w = hit_wall;
|
|
uint16_t const nw = wall[hit_wall].point2;
|
|
int16_t const wall_ang = NORM_ANGLE(getangle(wall[nw].x - wall[w].x, wall[nw].y - wall[w].y));
|
|
ang = sp->ang = NORM_ANGLE(wall_ang + 512);
|
|
}
|
|
else
|
|
ang = sp->ang;
|
|
#endif
|
|
|
|
#if 0
|
|
xvect = (sintable[(512 + ang) & 2047] >> 7);
|
|
yvect = (sintable[ang] >> 7);
|
|
move_missile(SpriteNum, xvect, yvect, 0L, CEILING_DIST, FLOOR_DIST, CLIPMASK_MISSILE, 4L);
|
|
#else
|
|
xvect = sintable[(512 + ang) & 2047] << 4;
|
|
yvect = sintable[ang] << 4;
|
|
|
|
clipmoveboxtracenum = 1;
|
|
|
|
// must have this
|
|
sectnum = sp->sectnum;
|
|
clipmove((vec3_t *)sp, §num, xvect, yvect,
|
|
4L, 4L<<8, 4L<<8, CLIPMASK_MISSILE);
|
|
clipmoveboxtracenum = 3;
|
|
|
|
if (sp->sectnum != sectnum)
|
|
changespritesect(SpriteNum, sectnum);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
InitUzi(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
SPRITEp wp, hsp;
|
|
USERp wu;
|
|
short daang, j;
|
|
hitdata_t hitinfo;
|
|
int daz, nz;
|
|
int xvect,yvect,zvect;
|
|
short cstat = 0;
|
|
uint8_t pal = 0;
|
|
//static char alternate=0;
|
|
static int uziclock=0;
|
|
int clockdiff=0;
|
|
SWBOOL FireSnd = FALSE;
|
|
#define UZIFIRE_WAIT 20
|
|
|
|
void InitUziShell(PLAYERp);
|
|
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
if (uziclock > PlayClock)
|
|
{
|
|
uziclock = PlayClock;
|
|
FireSnd = TRUE;
|
|
}
|
|
|
|
clockdiff = PlayClock - uziclock;
|
|
if (clockdiff > UZIFIRE_WAIT)
|
|
{
|
|
uziclock = PlayClock;
|
|
FireSnd = TRUE;
|
|
}
|
|
|
|
if (FireSnd)
|
|
PlaySound(DIGI_UZIFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (RANDOM_P2(1024) < 400)
|
|
InitTracerUzi(pp);
|
|
|
|
nz = pp->posz + pp->bob_z;
|
|
daz = pp->posz + pp->bob_z;
|
|
daang = 32;
|
|
if (WeaponAutoAimHitscan(pp->SpriteP, &daz, &daang, FALSE) != -1)
|
|
{
|
|
daang += RANDOM_RANGE(24) - 12;
|
|
daang = NORM_ANGLE(daang);
|
|
daz += RANDOM_RANGE(10000) - 5000;
|
|
}
|
|
else
|
|
{
|
|
//daang = NORM_ANGLE(FixedToInt(pp->q16ang) + (RANDOM_RANGE(50) - 25));
|
|
daang = NORM_ANGLE(FixedToInt(pp->q16ang) + (RANDOM_RANGE(24) - 12));
|
|
daz = ((100 - FixedToInt(pp->q16horiz)) * 2000) + (RANDOM_RANGE(24000) - 12000);
|
|
}
|
|
|
|
|
|
xvect = sintable[NORM_ANGLE(daang + 512)];
|
|
yvect = sintable[NORM_ANGLE(daang)];
|
|
zvect = daz;
|
|
FAFhitscan(pp->posx, pp->posy, nz, pp->cursectnum, // Start position
|
|
xvect,yvect,zvect,
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
SetVisHigh();
|
|
|
|
// check to see what you hit
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
if (labs(hitinfo.pos.z - sector[hitinfo.sect].ceilingz) <= Z(1))
|
|
{
|
|
hitinfo.pos.z += Z(16);
|
|
SET(cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
if (TEST(sector[hitinfo.sect].ceilingstat, CEILING_STAT_PLAX))
|
|
return 0;
|
|
|
|
if (SectorIsUnderwaterArea(hitinfo.sect))
|
|
{
|
|
WarpToSurface(&hitinfo.sect, &hitinfo.pos.x, &hitinfo.pos.y, &hitinfo.pos.z);
|
|
ContinueHitscan(pp, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, xvect, yvect, zvect);
|
|
return 0;
|
|
}
|
|
}
|
|
else if (labs(hitinfo.pos.z - sector[hitinfo.sect].floorz) <= Z(1))
|
|
{
|
|
if (TEST(sector[hitinfo.sect].extra, SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE)
|
|
{
|
|
SpawnSplashXY(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,hitinfo.sect);
|
|
|
|
if (SectorIsDiveArea(hitinfo.sect))
|
|
{
|
|
WarpToUnderwater(&hitinfo.sect, &hitinfo.pos.x, &hitinfo.pos.y, &hitinfo.pos.z);
|
|
ContinueHitscan(pp, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, xvect, yvect, zvect);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, u->ID);
|
|
return 0;
|
|
}
|
|
|
|
QueueHole(hitinfo.sect,hitinfo.wall,hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z);
|
|
}
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
USERp hu = User[hitinfo.sprite];
|
|
hsp = &sprite[hitinfo.sprite];
|
|
|
|
if (hu) // JBF: added null check
|
|
if (hu->ID == TRASHCAN)
|
|
{
|
|
extern STATE s_TrashCanPain[];
|
|
|
|
PlaySound(DIGI_TRASHLID, hsp, v3df_none);
|
|
if (hu->WaitTics <= 0)
|
|
{
|
|
hu->WaitTics = SEC(2);
|
|
ChangeState(hitinfo.sprite,s_TrashCanPain);
|
|
}
|
|
}
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_UZI, hitinfo.sprite))
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE) && HitBreakSprite(hitinfo.sprite,0))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (BulletHitSprite(pp->SpriteP, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, 0))
|
|
return 0;
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
}
|
|
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SMOKE_REPEAT;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
//SET(wp->cstat, cstat | CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, cstat | CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hitinfo.wall);
|
|
DoHitscanDamage(j, hitinfo.sprite);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SPARK_REPEAT;
|
|
wp->yrepeat = UZI_SPARK_REPEAT;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
wu->spal = wp->pal = pal;
|
|
SET(wp->cstat, cstat | CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hitinfo.wall);
|
|
|
|
if (RANDOM_P2(1024) < 100)
|
|
{
|
|
PlaySound(DIGI_RICHOCHET1,wp, v3df_none);
|
|
}
|
|
else if (RANDOM_P2(1024) < 100)
|
|
PlaySound(DIGI_RICHOCHET2,wp, v3df_none);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Electro Magnetic Pulse gun
|
|
int
|
|
InitEMP(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
SPRITEp wp, hsp=NULL;
|
|
USERp wu;
|
|
short daang, j;
|
|
hitdata_t hitinfo;
|
|
int daz, nz;
|
|
short cstat = 0;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
PlaySound(DIGI_RAILFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
InitTracerUzi(pp);
|
|
|
|
//daz = nz = pp->posz + Z(8) + ((100 - FixedToInt(pp->q16horiz)) * 72);
|
|
//daang = NORM_ANGLE(FixedToInt(pp->q16ang) + (RANDOM_RANGE(50) - 25));
|
|
|
|
daz = nz = pp->posz + pp->bob_z;
|
|
daang = 64;
|
|
if (WeaponAutoAimHitscan(pp->SpriteP, &daz, &daang, FALSE) != -1)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
daz = (100 - FixedToInt(pp->q16horiz)) * 2000;
|
|
daang = FixedToInt(pp->q16ang);
|
|
}
|
|
|
|
FAFhitscan(pp->posx, pp->posy, nz, pp->cursectnum, // Start position
|
|
sintable[NORM_ANGLE(daang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(daang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, EMP, s_EMPBurst, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
|
|
wu->WaitTics = SEC(7);
|
|
wp->shade = -127;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
SET(wp->cstat, cstat | CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
if (hitinfo.sect < 0)
|
|
{
|
|
////DSPRINTF(ds,"PROBLEM! - FAFhitscan returned a bad hitinfo.sect");
|
|
//MONO_PRINT(ds);
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
if (TEST(pp->Flags, PF_DIVING) ||
|
|
(hitinfo.wall < 0 && hitinfo.sprite < 0 && SectorIsDiveArea(hitinfo.sect))
|
|
)
|
|
{
|
|
InitUziBullet(pp);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
SetVisHigh();
|
|
|
|
// check to see what you hit
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
if (labs(hitinfo.pos.z - sector[hitinfo.sect].ceilingz) <= Z(1))
|
|
{
|
|
hitinfo.pos.z += Z(16);
|
|
SET(cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
if (TEST(sector[hitinfo.sect].ceilingstat, CEILING_STAT_PLAX))
|
|
return 0;
|
|
}
|
|
else if (labs(hitinfo.pos.z - sector[hitinfo.sect].floorz) <= Z(1))
|
|
{
|
|
if (TEST(sector[hitinfo.sect].extra, SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE)
|
|
{
|
|
SpawnSplashXY(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,hitinfo.sect);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
HitscanSpriteAdjust(j, hitinfo.wall);
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, u->ID);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
hsp = &sprite[hitinfo.sprite];
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_UZI, hitinfo.sprite))
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
//return(0);
|
|
}
|
|
|
|
if (BulletHitSprite(pp->SpriteP, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z,0))
|
|
//return(0);
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
// attach weapon to sprite
|
|
SetAttach(hitinfo.sprite, j);
|
|
wu->sz = sprite[hitinfo.sprite].z - wp->z;
|
|
if (RANDOM_RANGE(1000) > 500)
|
|
PlayerSound(DIGI_YOULOOKSTUPID, v3df_follow|v3df_dontpan,pp);
|
|
}
|
|
else
|
|
{
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
SET(wu->Flags2, SPR2_ATTACH_WALL);
|
|
}
|
|
else if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR))
|
|
{
|
|
// hit floor
|
|
if (wp->z > DIV2(wu->hiz + wu->loz))
|
|
SET(wu->Flags2, SPR2_ATTACH_FLOOR);
|
|
else
|
|
SET(wu->Flags2, SPR2_ATTACH_CEILING);
|
|
}
|
|
else
|
|
{
|
|
KillSprite(j);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitTankShell(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
short w;
|
|
|
|
if (!SW_SHAREWARE)
|
|
PlaySound(DIGI_CANNON, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
w = SpawnSprite(STAT_MISSILE, 0, s_TankShell, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, TANK_SHELL_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 8;
|
|
wp->xrepeat = 8;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 50;
|
|
wu->ceiling_dist = Z(4);
|
|
wu->floor_dist = Z(4);
|
|
SET(wu->Flags2, SPR2_SO_MISSILE);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * (wp->xvel/8));
|
|
|
|
WeaponAutoAim(sp, w, 64, FALSE);
|
|
// a bit of randomness
|
|
wp->ang += RANDOM_RANGE(30) - 15;
|
|
wp->ang = NORM_ANGLE(wp->ang);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitTurretMicro(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
|
|
USERp pu = User[pp->PlayerSprite];
|
|
USERp wu,hu;
|
|
SPRITEp wp,hp;
|
|
int nx, ny, nz, dist;
|
|
short w;
|
|
short i,ang;
|
|
TARGET_SORTp ts = TargetSort;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
|
|
#define MAX_TURRET_MICRO 10
|
|
|
|
DoPickTarget(pp->SpriteP, 256, FALSE);
|
|
|
|
if (TargetSortCount > MAX_TURRET_MICRO)
|
|
TargetSortCount = MAX_TURRET_MICRO;
|
|
|
|
for (i = 0; i < MAX_TURRET_MICRO; i++)
|
|
{
|
|
if (ts < &TargetSort[TargetSortCount] && ts->sprite_num >= 0)
|
|
{
|
|
hp = &sprite[ts->sprite_num];
|
|
hu = User[ts->sprite_num];
|
|
|
|
ang = getangle(hp->x - nx, hp->y - ny);
|
|
|
|
ts++;
|
|
}
|
|
else
|
|
{
|
|
hp = NULL;
|
|
hu = NULL;
|
|
ang = sp->ang;
|
|
}
|
|
|
|
nz = sp->z;
|
|
nz += Z(RANDOM_RANGE(20)) - Z(10);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Micro[0][0], sp->sectnum,
|
|
nx, ny, nz, ang, 1200);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 24;
|
|
wp->xrepeat = 24;
|
|
wp->shade = -15;
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
wp->clipdist = 64L>>2;
|
|
|
|
// randomize zvelocity
|
|
wp->zvel += RANDOM_RANGE(Z(8)) - Z(5);
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Micro[0]);
|
|
|
|
wu->WeaponNum = pu->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(2);
|
|
wu->floor_dist = Z(2);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
wu->WaitTics = 10 + RANDOM_RANGE(40);
|
|
|
|
#define MICRO_ANG 400
|
|
|
|
if (hp)
|
|
{
|
|
dist = Distance(wp->x, wp->y, hp->x, hp->y);
|
|
if (dist != 0)
|
|
{
|
|
int zh;
|
|
zh = SPRITEp_TOS(hp) + DIV4(SPRITEp_SIZE_Z(hp));
|
|
wp->zvel = (wp->xvel * (zh - wp->z)) / dist;
|
|
}
|
|
|
|
wu->WpnGoal = ts->sprite_num;
|
|
SET(hu->Flags, SPR_TARGETED);
|
|
SET(hu->Flags, SPR_ATTACKED);
|
|
}
|
|
else
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang + (RANDOM_RANGE(MICRO_ANG) - DIV2(MICRO_ANG)) - 16);
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitTurretRocket(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
short w;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, &s_Rocket[0][0], sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, ROCKET_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 40;
|
|
wp->xrepeat = 40;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 50;
|
|
wu->ceiling_dist = Z(4);
|
|
wu->floor_dist = Z(4);
|
|
SET(wu->Flags2, SPR2_SO_MISSILE);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * (wp->xvel/8));
|
|
|
|
WeaponAutoAim(sp, w, 64, FALSE);
|
|
// a bit of randomness
|
|
//wp->ang += RANDOM_RANGE(30) - 15;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitTurretFireball(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
short w;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
w = SpawnSprite(STAT_MISSILE, FIREBALL, s_Fireball, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, FIREBALL_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 40;
|
|
wp->xrepeat = 40;
|
|
wp->shade = -40;
|
|
wp->zvel = 0;
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 50;
|
|
wu->ceiling_dist = Z(4);
|
|
wu->floor_dist = Z(4);
|
|
SET(wu->Flags2, SPR2_SO_MISSILE);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * (wp->xvel/8));
|
|
|
|
WeaponAutoAim(sp, w, 64, FALSE);
|
|
// a bit of randomness
|
|
wp->ang += RANDOM_RANGE(30) - 15;
|
|
wp->ang = NORM_ANGLE(wp->ang);
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitTurretRail(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R1, &s_Rail[0][0], pp->cursectnum,
|
|
nx, ny, nz, sp->ang, 1200);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 52;
|
|
wp->xrepeat = 52;
|
|
wp->shade = -15;
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Rail[0]);
|
|
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wu->Flags2, SPR2_SO_MISSILE);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER|CSTAT_SPRITE_INVISIBLE);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
wp->clipdist = 64L>>2;
|
|
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang);
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitTurretLaser(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
if (SW_SHAREWARE) return FALSE; // JBF: verify
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z;
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, BOLT_THINMAN_R0, s_Laser, pp->cursectnum,
|
|
nx, ny, nz, sp->ang, 300);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 52;
|
|
wp->xrepeat = 52;
|
|
wp->shade = -15;
|
|
|
|
// the slower the missile travels the less of a zvel it needs
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
wp->zvel /= 4;
|
|
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(1);
|
|
wu->floor_dist = Z(1);
|
|
SET(wu->Flags2, SPR2_SO_MISSILE);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
wp->clipdist = 64L>>2;
|
|
|
|
if (WeaponAutoAim(sp, w, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang);
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSobjMachineGun(short SpriteNum, PLAYERp pp)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = u->SpriteP;
|
|
short daang;
|
|
hitdata_t hitinfo;
|
|
short nsect;
|
|
int daz;
|
|
int nx,ny,nz;
|
|
short cstat = 0;
|
|
short spark;
|
|
//static sound = 0;
|
|
|
|
//sound = (++sound)&1;
|
|
//if (sound == 0)
|
|
PlaySound(DIGI_BOATFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
daz = nz = sp->z;
|
|
nsect = sp->sectnum;
|
|
|
|
if (RANDOM_P2(1024) < 200)
|
|
InitTracerTurret(sp - sprite, pp->PlayerSprite, FixedToInt(pp->q16horiz));
|
|
|
|
daang = 64;
|
|
if (WeaponAutoAimHitscan(sp, &daz, &daang, FALSE) != -1)
|
|
{
|
|
daz += RANDOM_RANGE(Z(30)) - Z(15);
|
|
//daz += 0;
|
|
}
|
|
else
|
|
{
|
|
int horiz;
|
|
horiz = FixedToInt(pp->q16horiz);
|
|
if (horiz < 75)
|
|
horiz = 75;
|
|
|
|
daz = ((100 - horiz) * 2000) + (RANDOM_RANGE(Z(80)) - Z(40));
|
|
daang = sp->ang;
|
|
}
|
|
|
|
FAFhitscan(nx, ny, nz, nsect, // Start position
|
|
sintable[NORM_ANGLE(daang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(daang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
{
|
|
//DSPRINTF(ds,"PROBLEM! - FAFhitscan returned a bad hitinfo.sect");
|
|
MONO_PRINT(ds);
|
|
return 0;
|
|
}
|
|
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
if (labs(hitinfo.pos.z - sector[hitinfo.sect].ceilingz) <= Z(1))
|
|
{
|
|
hitinfo.pos.z += Z(16);
|
|
SET(cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
if (TEST(sector[hitinfo.sect].ceilingstat, CEILING_STAT_PLAX))
|
|
return 0;
|
|
}
|
|
else if (labs(hitinfo.pos.z - sector[hitinfo.sect].floorz) <= Z(1))
|
|
{
|
|
if (TEST(sector[hitinfo.sect].extra, SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE)
|
|
{
|
|
SpawnSplashXY(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,hitinfo.sect);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
SPRITEp hsp = &sprite[hitinfo.sprite];
|
|
|
|
if (sprite[hitinfo.sprite].lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
// spawn sparks here and pass the sprite as SO_MISSILE
|
|
spark = SpawnBoatSparks(pp, hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
SET(User[spark]->Flags2, SPR2_SO_MISSILE);
|
|
if (MissileHitMatch(spark, -1, hitinfo.sprite))
|
|
return 0;
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
return 0;
|
|
}
|
|
|
|
if (BulletHitSprite(pp->SpriteP, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, 0))
|
|
return 0;
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
}
|
|
|
|
spark = SpawnBoatSparks(pp, hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
DoHitscanDamage(spark, hitinfo.sprite);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSobjGun(PLAYERp pp)
|
|
{
|
|
short i;
|
|
SPRITEp sp;
|
|
SWBOOL first = FALSE;
|
|
|
|
for (i = 0; pp->sop->sp_num[i] != -1; i++)
|
|
{
|
|
if (sprite[pp->sop->sp_num[i]].statnum == STAT_SO_SHOOT_POINT)
|
|
{
|
|
sp = &sprite[pp->sop->sp_num[i]];
|
|
|
|
// match when firing
|
|
if (SP_TAG2(sp))
|
|
{
|
|
DoMatchEverything(pp, SP_TAG2(sp), -1);
|
|
if (TEST_BOOL1(sp))
|
|
{
|
|
SP_TAG2(sp) = 0;
|
|
}
|
|
}
|
|
|
|
// inert shoot point
|
|
if ((uint8_t)SP_TAG3(sp) == 255)
|
|
return 0;
|
|
|
|
if (!first)
|
|
{
|
|
first = TRUE;
|
|
if (SP_TAG6(sp))
|
|
DoSoundSpotMatch(SP_TAG6(sp), 1, SOUND_OBJECT_TYPE);
|
|
}
|
|
|
|
switch (SP_TAG3(sp))
|
|
{
|
|
case 32:
|
|
case 0:
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 8);
|
|
SpawnBigGunFlames(sp - sprite, pp->PlayerSprite, pp->sop);
|
|
SetGunQuake(sp - sprite);
|
|
InitTankShell(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 80;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
case 1:
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 32);
|
|
SpawnBigGunFlames(-(sp - sprite), pp->PlayerSprite, pp->sop);
|
|
InitSobjMachineGun(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 10;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
case 2:
|
|
if (SW_SHAREWARE) break;
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 32);
|
|
InitTurretLaser(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 120;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
case 3:
|
|
if (SW_SHAREWARE) break;
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 32);
|
|
InitTurretRail(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 120;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
case 4:
|
|
if (SW_SHAREWARE) break;
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 32);
|
|
InitTurretFireball(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 20;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
case 5:
|
|
if (SW_SHAREWARE) break;
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 32);
|
|
InitTurretRocket(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 100;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
case 6:
|
|
if (SW_SHAREWARE) break;
|
|
SpawnVis(sp - sprite, -1, -1, -1, -1, 32);
|
|
InitTurretMicro(sp - sprite, pp);
|
|
if (!SP_TAG5(sp))
|
|
pp->FirePause = 100;
|
|
else
|
|
pp->FirePause = SP_TAG5(sp);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnBoatSparks(PLAYERp pp, short hit_sect, short hit_wall, int hit_x, int hit_y, int hit_z, short hit_ang)
|
|
{
|
|
short j;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SMOKE_REPEAT + 12;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT + 12;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
// Sprite starts out with center exactly on wall.
|
|
// This moves it back enough to see it at all angles.
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SPARK_REPEAT + 10;
|
|
wp->yrepeat = UZI_SPARK_REPEAT + 10;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
wu->spal = wp->pal = PALETTE_DEFAULT;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
if (RANDOM_P2(1024) < 100)
|
|
PlaySound(DIGI_RICHOCHET1,wp, v3df_none);
|
|
|
|
return j;
|
|
}
|
|
|
|
int
|
|
SpawnSwordSparks(PLAYERp pp, short hit_sect, short hit_wall, int hit_x, int hit_y, int hit_z, short hit_ang)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
short j;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = wp->yrepeat = 20;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
|
|
// Sprite starts out with center exactly on wall.
|
|
// This moves it back enough to see it at all angles.
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
if (hit_wall != -1)
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = wp->yrepeat = 20;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
wu->spal = wp->pal = PALETTE_DEFAULT;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
if (u->WeaponNum == WPN_FIST)
|
|
SET(wp->cstat, CSTAT_SPRITE_INVISIBLE);
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
if (hit_wall != -1)
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnTurretSparks(/*SPRITEp sp, */short hit_sect, short hit_wall, int hit_x, int hit_y, int hit_z, short hit_ang)
|
|
{
|
|
//USERp u = User[sp - sprite];
|
|
short j;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SMOKE_REPEAT + 12;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT + 12;
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
|
|
// Sprite starts out with center exactly on wall.
|
|
// This moves it back enough to see it at all angles.
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SPARK_REPEAT + 10;
|
|
wp->yrepeat = UZI_SPARK_REPEAT + 10;
|
|
wu->spal = wp->pal = PALETTE_DEFAULT;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
if (RANDOM_P2(1024) < 100)
|
|
PlaySound(DIGI_RICHOCHET1, wp, v3df_none);
|
|
|
|
return j;
|
|
}
|
|
|
|
int
|
|
SpawnShotgunSparks(PLAYERp pp, short hit_sect, short hit_wall, int hit_x, int hit_y, int hit_z, short hit_ang)
|
|
{
|
|
short j;
|
|
SPRITEp wp;
|
|
USERp wu;
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SPARK_REPEAT;
|
|
wp->yrepeat = UZI_SPARK_REPEAT;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
wu->spal = wp->pal = PALETTE_DEFAULT;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, SHOTGUN_SMOKE, s_ShotgunSmoke, hit_sect, hit_x, hit_y, hit_z, hit_ang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = SHOTGUN_SMOKE_REPEAT;
|
|
wp->yrepeat = SHOTGUN_SMOKE_REPEAT;
|
|
SetOwner(pp->PlayerSprite, j);
|
|
SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
// Sprite starts out with center exactly on wall.
|
|
// This moves it back enough to see it at all angles.
|
|
|
|
wp->clipdist = 32 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hit_wall);
|
|
|
|
return j;
|
|
}
|
|
|
|
int
|
|
InitTurretMgun(SECTOR_OBJECTp sop)
|
|
{
|
|
SPRITEp hsp;
|
|
short daang, i, j;
|
|
hitdata_t hitinfo;
|
|
short nsect;
|
|
int daz;
|
|
int nx,ny,nz;
|
|
short cstat = 0;
|
|
short delta;
|
|
SPRITEp sp;
|
|
int xvect,yvect,zvect;
|
|
|
|
PlaySound(DIGI_BOATFIRE, (vec3_t*)sop, v3df_dontpan|v3df_doppler);
|
|
|
|
for (i = 0; sop->sp_num[i] != -1; i++)
|
|
{
|
|
if (sprite[sop->sp_num[i]].statnum == STAT_SO_SHOOT_POINT)
|
|
{
|
|
sp = &sprite[sop->sp_num[i]];
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
daz = nz = sp->z;
|
|
nsect = sp->sectnum;
|
|
|
|
// if its not operated by a player
|
|
if (sop->Animator)
|
|
{
|
|
// only auto aim for Z
|
|
daang = 512;
|
|
if ((hitinfo.sprite = WeaponAutoAimHitscan(sp, &daz, &daang, FALSE)) != -1)
|
|
{
|
|
delta = labs(GetDeltaAngle(daang, sp->ang));
|
|
if (delta > 128)
|
|
{
|
|
// don't shoot if greater than 128
|
|
return 0;
|
|
}
|
|
else if (delta > 24)
|
|
{
|
|
// always shoot the ground when tracking
|
|
// and not close
|
|
WeaponHitscanShootFeet(sp, &sprite[hitinfo.sprite], &daz);
|
|
|
|
daang = sp->ang;
|
|
daang = NORM_ANGLE(daang + RANDOM_P2(32) - 16);
|
|
}
|
|
else
|
|
{
|
|
// randomize the z for shots
|
|
daz += RANDOM_RANGE(Z(120)) - Z(60);
|
|
// never auto aim the angle
|
|
daang = sp->ang;
|
|
daang = NORM_ANGLE(daang + RANDOM_P2(64) - 32);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
daang = 64;
|
|
if (WeaponAutoAimHitscan(sp, &daz, &daang, FALSE) != -1)
|
|
{
|
|
daz += RANDOM_RANGE(Z(30)) - Z(15);
|
|
}
|
|
}
|
|
|
|
xvect = sintable[NORM_ANGLE(daang + 512)];
|
|
yvect = sintable[NORM_ANGLE(daang)];
|
|
zvect = daz;
|
|
|
|
FAFhitscan(nx, ny, nz, nsect, // Start position
|
|
xvect, yvect, zvect,
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (RANDOM_P2(1024) < 400)
|
|
{
|
|
InitTracerAutoTurret(sop->sp_num[i], -1,
|
|
xvect>>4, yvect>>4, zvect>>4);
|
|
}
|
|
|
|
if (hitinfo.sect < 0)
|
|
continue;
|
|
|
|
if (hitinfo.sprite < 0 && hitinfo.wall < 0)
|
|
{
|
|
if (labs(hitinfo.pos.z - sector[hitinfo.sect].ceilingz) <= Z(1))
|
|
{
|
|
hitinfo.pos.z += Z(16);
|
|
SET(cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
if (TEST(sector[hitinfo.sect].ceilingstat, CEILING_STAT_PLAX))
|
|
continue;
|
|
}
|
|
else if (labs(hitinfo.pos.z - sector[hitinfo.sect].floorz) <= Z(1))
|
|
{
|
|
if (TEST(sector[hitinfo.sect].extra, SECTFX_LIQUID_MASK) != SECTFX_LIQUID_NONE)
|
|
{
|
|
SpawnSplashXY(hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z,hitinfo.sect);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
continue;
|
|
}
|
|
|
|
QueueHole(hitinfo.sect,hitinfo.wall,hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z);
|
|
}
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
hsp = &sprite[hitinfo.sprite];
|
|
|
|
if (hsp->lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
if (MissileHitMatch(-1, WPN_UZI, hitinfo.sprite))
|
|
continue;
|
|
}
|
|
|
|
if (TEST(hsp->extra, SPRX_BREAKABLE))
|
|
{
|
|
HitBreakSprite(hitinfo.sprite,0);
|
|
continue;
|
|
}
|
|
|
|
if (BulletHitSprite(sp, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, 0))
|
|
continue;
|
|
|
|
// hit a switch?
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL) && (hsp->lotag || hsp->hitag))
|
|
{
|
|
ShootableSwitch(hitinfo.sprite);
|
|
}
|
|
}
|
|
|
|
|
|
j = SpawnTurretSparks(/*sp, */hitinfo.sect, hitinfo.wall, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang);
|
|
DoHitscanDamage(j, hitinfo.sprite);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitEnemyUzi(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP,wp;
|
|
USERp u = User[SpriteNum];
|
|
USERp wu;
|
|
short daang, j;
|
|
hitdata_t hitinfo = { { -2, -2, -2 }, -2, -2, -2 };
|
|
int daz;
|
|
int zh;
|
|
void InitUziShell(PLAYERp);
|
|
static short alternate;
|
|
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
setspritez(SpriteNum, (vec3_t *)sp);
|
|
|
|
if (u->ID == ZILLA_RUN_R0)
|
|
{
|
|
zh = SPRITEp_TOS(sp);
|
|
zh += Z(20);
|
|
}
|
|
else
|
|
{
|
|
zh = SPRITEp_SIZE_Z(sp);
|
|
zh -= DIV4(zh);
|
|
}
|
|
daz = sp->z - zh;
|
|
|
|
if (AimHitscanToTarget(sp, &daz, &daang, 200) != -1)
|
|
{
|
|
// set angle to player and also face player when attacking
|
|
sp->ang = daang;
|
|
daang += RANDOM_RANGE(24) - 12;
|
|
daang = NORM_ANGLE(daang);
|
|
daz += RANDOM_RANGE(Z(40)) - Z(20);
|
|
}
|
|
else
|
|
{
|
|
// couldn't shoot target for some reason
|
|
|
|
// don't bother wasting processing 50% of the time
|
|
if (RANDOM_P2(1024) < 512)
|
|
return 0;
|
|
|
|
daz = 0;
|
|
daang = NORM_ANGLE(sp->ang + (RANDOM_P2(128)) - 64);
|
|
}
|
|
|
|
FAFhitscan(sp->x, sp->y, sp->z - zh, sp->sectnum, // Start position
|
|
sintable[NORM_ANGLE(daang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(daang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
return 0;
|
|
|
|
if (RANDOM_P2(1024<<4)>>4 > 700)
|
|
{
|
|
if (u->ID == TOILETGIRL_R0 || u->ID == WASHGIRL_R0 || u->ID == CARGIRL_R0)
|
|
SpawnShell(SpriteNum,-3);
|
|
else
|
|
SpawnShell(SpriteNum,-2); // Enemy Uzi shell
|
|
}
|
|
|
|
if ((alternate++)>2) alternate = 0;
|
|
if (!alternate)
|
|
{
|
|
if (sp->pal == PALETTE_PLAYER3 || sp->pal == PALETTE_PLAYER5 ||
|
|
sp->pal == PAL_XLAT_LT_GREY || sp->pal == PAL_XLAT_LT_TAN)
|
|
PlaySound(DIGI_M60, sp, v3df_none);
|
|
else
|
|
PlaySound(DIGI_NINJAUZIATTACK, sp, v3df_none);
|
|
}
|
|
|
|
if (hitinfo.wall >= 0)
|
|
{
|
|
if (wall[hitinfo.wall].nextsector >= 0)
|
|
{
|
|
if (TEST(sector[wall[hitinfo.wall].nextsector].ceilingstat, CEILING_STAT_PLAX))
|
|
{
|
|
if (hitinfo.pos.z < sector[wall[hitinfo.wall].nextsector].ceilingz)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (wall[hitinfo.wall].lotag == TAG_WALL_BREAK)
|
|
{
|
|
HitBreakWall(&wall[hitinfo.wall], hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, u->ID);
|
|
return 0;
|
|
}
|
|
|
|
QueueHole(hitinfo.sect,hitinfo.wall,hitinfo.pos.x,hitinfo.pos.y,hitinfo.pos.z);
|
|
}
|
|
|
|
if (hitinfo.sprite >= 0)
|
|
{
|
|
if (BulletHitSprite(sp, hitinfo.sprite, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, 0))
|
|
return 0;
|
|
}
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE+2, s_UziSmoke, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SMOKE_REPEAT;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT;
|
|
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
SetOwner(sp->owner, j);
|
|
else
|
|
SetOwner(SpriteNum, j);
|
|
|
|
User[j]->WaitTics = 63;
|
|
//SET(wp->cstat, CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
|
|
wp->clipdist = 32L >> 2;
|
|
//HitscanSpriteAdjust(j, hitinfo.wall);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SMOKE, s_UziSmoke, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
wp = &sprite[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SMOKE_REPEAT;
|
|
wp->yrepeat = UZI_SMOKE_REPEAT;
|
|
SetOwner(SpriteNum, j);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hitinfo.wall);
|
|
DoHitscanDamage(j, hitinfo.sprite);
|
|
|
|
j = SpawnSprite(STAT_MISSILE, UZI_SPARK, s_UziSpark, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, daang, 0);
|
|
wp = &sprite[j];
|
|
wu = User[j];
|
|
wp->shade = -40;
|
|
wp->xrepeat = UZI_SPARK_REPEAT;
|
|
wp->yrepeat = UZI_SPARK_REPEAT;
|
|
SetOwner(SpriteNum, j);
|
|
wu->spal = wp->pal;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
wp->clipdist = 8 >> 2;
|
|
|
|
HitscanSpriteAdjust(j, hitinfo.wall);
|
|
|
|
if (RANDOM_P2(1024) < 100)
|
|
{
|
|
PlaySound(DIGI_RICHOCHET1,wp, v3df_none);
|
|
}
|
|
else if (RANDOM_P2(1024) < 100)
|
|
PlaySound(DIGI_RICHOCHET2,wp, v3df_none);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitGrenade(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
short oclipdist;
|
|
int zvel;
|
|
SWBOOL auto_aim = FALSE;
|
|
|
|
DoPlayerBeginRecoil(pp, GRENADE_RECOIL_AMT);
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
PlaySound(DIGI_30MMFIRE, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, GRENADE, &s_Grenade[0][0], pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), GRENADE_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
// don't throw it as far if crawling
|
|
if (TEST(pp->Flags, PF_CRAWLING))
|
|
{
|
|
wp->xvel -= DIV4(wp->xvel);
|
|
}
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Grenade[0]);
|
|
SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 32;
|
|
wp->xrepeat = 32;
|
|
wp->shade = -15;
|
|
//wp->clipdist = 80L>>2;
|
|
wp->clipdist = 32L>>2;
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
wu->Counter = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
|
|
////DSPRINTF(ds,"horiz %d, ho %d, ho+ho %d",FixedToInt(pp->q16horiz), FixedToInt(pp->q16horizoff), FixedToInt(pp->q16horizoff + pp->q16horiz));
|
|
//MONO_PRINT(ds);
|
|
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 800);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
// don't do smoke for this movement
|
|
SET(wu->Flags, SPR_BOUNCE);
|
|
MissileSetPos(w, DoGrenade, 1000);
|
|
RESET(wu->Flags, SPR_BOUNCE);
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
// int dist;
|
|
//dist = FindDistance2D(pp->xvect, pp->yvect)>>12;
|
|
//dist = dist - (dist/2);
|
|
|
|
zvel = wp->zvel;
|
|
if (WeaponAutoAim(pp->SpriteP, w, 32, FALSE) >= 0)
|
|
{
|
|
auto_aim = TRUE;
|
|
}
|
|
wp->zvel = zvel;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
if (!auto_aim)
|
|
{
|
|
// adjust xvel according to player velocity
|
|
wu->xchange += pp->xvect>>14;
|
|
wu->ychange += pp->yvect>>14;
|
|
}
|
|
|
|
wu->Counter2 = TRUE; // Phosphorus Grenade
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitSpriteGrenade(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
|
|
PlaySound(DIGI_30MMFIRE, sp, v3df_dontpan|v3df_doppler);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - Z(40);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
w = SpawnSprite(STAT_MISSILE, GRENADE, &s_Grenade[0][0], sp->sectnum,
|
|
nx, ny, nz, sp->ang, GRENADE_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wu->RotNum = 5;
|
|
NewStateGroup(w, &sg_Grenade[0]);
|
|
SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
if (u->ID == ZOMBIE_RUN_R0)
|
|
SetOwner(sp->owner, w);
|
|
else
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 32;
|
|
wp->xrepeat = 32;
|
|
wp->shade = -15;
|
|
//wp->clipdist = 80L>>2;
|
|
wp->clipdist = 32L>>2;
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(3);
|
|
wu->floor_dist = Z(3);
|
|
wu->Counter = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(wp->cstat, CSTAT_SPRITE_BLOCK);
|
|
|
|
|
|
//wp->zvel = (-RANDOM_RANGE(100) * HORIZ_MULT);
|
|
wp->zvel = -2000;
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = wp->zvel;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 800);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
// don't do smoke for this movement
|
|
SET(wu->Flags, SPR_BOUNCE);
|
|
MissileSetPos(w, DoGrenade, 400);
|
|
RESET(wu->Flags, SPR_BOUNCE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitMine(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
int dot;
|
|
|
|
PlayerUpdateAmmo(pp, u->WeaponNum, -1);
|
|
|
|
PlaySound(DIGI_MINETHROW, pp, v3df_dontpan|v3df_doppler);
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx = pp->posx;
|
|
ny = pp->posy;
|
|
nz = pp->posz + pp->bob_z + Z(8);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
|
|
w = SpawnSprite(STAT_MISSILE, MINE, s_Mine, pp->cursectnum,
|
|
nx, ny, nz, FixedToInt(pp->q16ang), MINE_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(pp->PlayerSprite, w);
|
|
wp->yrepeat = 32;
|
|
wp->xrepeat = 32;
|
|
wp->shade = -15;
|
|
wp->clipdist = 128L>>2;
|
|
wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(5);
|
|
wu->floor_dist = Z(5);
|
|
wu->Counter = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
wu->spal = wp->pal = User[pp->PlayerSprite]->spal; // Set sticky color
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
//wp->zvel = ((100 - FixedToInt(pp->q16horiz)) * HORIZ_MULT);
|
|
|
|
MissileSetPos(w, DoMine, 800);
|
|
|
|
wu->zchange = wp->zvel>>1;
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
|
|
dot = DOT_PRODUCT_2D(pp->xvect, pp->yvect, sintable[NORM_ANGLE(FixedToInt(pp->q16ang)+512)], sintable[FixedToInt(pp->q16ang)]);
|
|
|
|
// don't adjust for strafing
|
|
if (labs(dot) > 10000)
|
|
{
|
|
// adjust xvel according to player velocity
|
|
wu->xchange += pp->xvect>>13;
|
|
wu->ychange += pp->yvect>>13;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitEnemyMine(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
int nx, ny, nz;
|
|
short w;
|
|
|
|
|
|
PlaySound(DIGI_MINETHROW, sp, v3df_dontpan|v3df_doppler);
|
|
|
|
nx = sp->x;
|
|
ny = sp->y;
|
|
nz = sp->z - Z(40);
|
|
|
|
// Spawn a shot
|
|
// Inserting and setting up variables
|
|
w = SpawnSprite(STAT_MISSILE, MINE, s_Mine, sp->sectnum,
|
|
nx, ny, nz, sp->ang, MINE_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
SetOwner(SpriteNum, w);
|
|
wp->yrepeat = 32;
|
|
wp->xrepeat = 32;
|
|
wp->shade = -15;
|
|
wp->clipdist = 128L>>2;
|
|
|
|
wu->WeaponNum = u->WeaponNum;
|
|
wu->Radius = 200;
|
|
wu->ceiling_dist = Z(5);
|
|
wu->floor_dist = Z(5);
|
|
wu->Counter = 0;
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(wp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
wu->spal = wp->pal = User[SpriteNum]->spal; // Set sticky color
|
|
|
|
if (SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
MissileSetPos(w, DoMine, 300);
|
|
wp->ang = NORM_ANGLE(wp->ang-512);
|
|
MissileSetPos(w, DoMine, 300);
|
|
wp->ang = NORM_ANGLE(wp->ang+512);
|
|
|
|
wu->zchange = -5000;
|
|
//CON_Message("change = %ld",wu->zchange);
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
HelpMissileLateral(int16_t Weapon, int dist)
|
|
{
|
|
SPRITEp sp = &sprite[Weapon];
|
|
USERp u = User[Weapon];
|
|
int xchange, ychange;
|
|
short old_xvel;
|
|
short old_clipdist;
|
|
|
|
old_xvel = sp->xvel;
|
|
old_clipdist = sp->clipdist;
|
|
|
|
sp->xvel = dist;
|
|
xchange = MOVEx(sp->xvel, sp->ang);
|
|
ychange = MOVEy(sp->xvel, sp->ang);
|
|
|
|
sp->clipdist = 32L >> 2;
|
|
|
|
//u->ret = move_missile(Weapon, xchange, ychange, 0, Z(16), Z(16), CLIPMASK_MISSILE, 1);
|
|
u->ret = move_missile(Weapon, xchange, ychange, 0, Z(16), Z(16), 0, 1);
|
|
|
|
// !JIM! Should this be fatal, it seems to fail check alot, but doesn't crash game or anything
|
|
// when I commented it out.
|
|
// ASSERT(!u->ret);
|
|
|
|
sp->xvel = old_xvel;
|
|
sp->clipdist = old_clipdist;
|
|
|
|
u->ox = sp->x;
|
|
u->oy = sp->y;
|
|
u->oz = sp->z;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
InitFireball(PLAYERp pp)
|
|
{
|
|
USERp u = User[pp->PlayerSprite];
|
|
SPRITEp wp;
|
|
int nx = 0, ny = 0, nz;
|
|
short w;
|
|
USERp wu;
|
|
short oclipdist;
|
|
int zvel;
|
|
|
|
PlayerUpdateAmmo(pp, WPN_HOTHEAD, -1);
|
|
|
|
PlaySound(DIGI_HEADFIRE, pp, v3df_none);
|
|
|
|
// Make sprite shade brighter
|
|
u->Vis = 128;
|
|
|
|
if (pp->cursectnum < 0)
|
|
return 0;
|
|
|
|
nx += pp->posx;
|
|
ny += pp->posy;
|
|
|
|
nz = pp->posz + pp->bob_z + Z(15);
|
|
|
|
w = SpawnSprite(STAT_MISSILE, FIREBALL1, s_Fireball, pp->cursectnum, nx, ny, nz, FixedToInt(pp->q16ang), FIREBALL_VELOCITY);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
wp->xrepeat = 40;
|
|
wp->yrepeat = 40;
|
|
wp->shade = -40;
|
|
wp->clipdist = 32>>2;
|
|
SetOwner(pp->PlayerSprite, w);
|
|
SET(wp->cstat, CSTAT_SPRITE_YCENTER);
|
|
wu->Radius = 100;
|
|
|
|
wu->ceiling_dist = Z(6);
|
|
wu->floor_dist = Z(6);
|
|
//zvel = ((100 - FixedToInt(pp->q16horiz)) * (100+ADJUST));
|
|
zvel = ((100 - FixedToInt(pp->q16horiz)) * (240L));
|
|
|
|
//wu->RotNum = 5;
|
|
//NewStateGroup(w, &sg_Fireball);
|
|
//SET(wu->Flags, SPR_XFLIP_TOGGLE);
|
|
|
|
// at certain angles the clipping box was big enough to block the
|
|
// initial positioning of the fireball.
|
|
oclipdist = pp->SpriteP->clipdist;
|
|
pp->SpriteP->clipdist = 0;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + 512);
|
|
HelpMissileLateral(w, 2100);
|
|
wp->ang = NORM_ANGLE(wp->ang - 512);
|
|
|
|
if (TEST(pp->Flags, PF_DIVING) || SpriteInUnderwaterArea(wp))
|
|
SET(wu->Flags, SPR_UNDERWATER);
|
|
|
|
if (TestMissileSetPos(w, DoFireball, 1200, mulscale16(zvel,44000)))
|
|
{
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
KillSprite(w);
|
|
return 0;
|
|
}
|
|
|
|
pp->SpriteP->clipdist = oclipdist;
|
|
|
|
wp->zvel = zvel >> 1;
|
|
if (WeaponAutoAimZvel(pp->SpriteP, w, &zvel, 32, FALSE) == -1)
|
|
{
|
|
wp->ang = NORM_ANGLE(wp->ang - 9);
|
|
}
|
|
|
|
wu->xchange = MOVEx(wp->xvel, wp->ang);
|
|
wu->ychange = MOVEy(wp->xvel, wp->ang);
|
|
wu->zchange = zvel;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
InitEnemyFireball(short SpriteNum)
|
|
{
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, fp = NULL;
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp wp;
|
|
int nz, dist;
|
|
int size_z;
|
|
short w;
|
|
USERp wu;
|
|
SPRITEp tsp;
|
|
int i, targ_z, xchange, ychange;
|
|
|
|
static short lat_ang[] =
|
|
{
|
|
512, -512
|
|
};
|
|
|
|
ASSERT(SpriteNum >= 0);
|
|
|
|
tsp = u->tgt_sp;
|
|
|
|
PlaySound(DIGI_FIREBALL1, sp, v3df_none);
|
|
|
|
// get angle to player and also face player when attacking
|
|
sp->ang = NORM_ANGLE(getangle(tsp->x - sp->x, tsp->y - sp->y));
|
|
|
|
size_z = Z(SPRITEp_SIZE_Y(sp));
|
|
//nz = sp->z - size_z + DIV4(size_z) + DIV8(size_z);
|
|
nz = sp->z - size_z + DIV4(size_z) + DIV8(size_z) + Z(4);
|
|
|
|
xchange = MOVEx(GORO_FIREBALL_VELOCITY, sp->ang);
|
|
ychange = MOVEy(GORO_FIREBALL_VELOCITY, sp->ang);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
w = SpawnSprite(STAT_MISSILE, GORO_FIREBALL, s_Fireball, sp->sectnum,
|
|
sp->x, sp->y, nz, sp->ang, GORO_FIREBALL_VELOCITY);
|
|
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
wp->hitag = LUMINOUS; //Always full brightness
|
|
wp->xrepeat = 20;
|
|
wp->yrepeat = 20;
|
|
wp->shade = -40;
|
|
//wp->owner = SpriteNum;
|
|
SetOwner(SpriteNum, w);
|
|
wp->zvel = 0;
|
|
wp->clipdist = 16>>2;
|
|
|
|
wp->ang = NORM_ANGLE(wp->ang + lat_ang[i]);
|
|
HelpMissileLateral(w, 500L);
|
|
wp->ang = NORM_ANGLE(wp->ang - lat_ang[i]);
|
|
|
|
wu->xchange = xchange;
|
|
wu->ychange = ychange;
|
|
|
|
//MissileSetPos(w, DoFireball, 700);
|
|
MissileSetPos(w, DoFireball, 700);
|
|
|
|
if (i == 0)
|
|
{
|
|
// back up first one
|
|
fp = wp;
|
|
|
|
// find the distance to the target (player)
|
|
dist = ksqrt(SQ(wp->x - tsp->x) + SQ(wp->y - tsp->y));
|
|
//dist = Distance(wp->x, wp->y, tsp->x, tsp->y);
|
|
|
|
// Determine target Z value
|
|
//targ_z = tsp->z - Z(SPRITEp_SIZE_Y(sp)) + Z(DIV2(SPRITEp_SIZE_Y(sp)));
|
|
//targ_z = tsp->z;
|
|
targ_z = tsp->z - DIV2(Z(SPRITEp_SIZE_Y(sp)));
|
|
|
|
// (velocity * difference between the target and the throwing star) /
|
|
// distance
|
|
if (dist != 0)
|
|
wu->zchange = wp->zvel = (GORO_FIREBALL_VELOCITY * (targ_z - wp->z)) / dist;
|
|
}
|
|
else
|
|
{
|
|
// use the first calculations so the balls stay together
|
|
wu->zchange = wp->zvel = fp->zvel;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// for hitscans or other uses
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
SWBOOL
|
|
WarpToUnderwater(short *sectnum, int *x, int *y, int *z)
|
|
{
|
|
short i, nexti;
|
|
SECT_USERp sectu = SectUser[*sectnum];
|
|
SPRITEp under_sp = NULL, over_sp = NULL;
|
|
char Found = FALSE;
|
|
short over, under;
|
|
int sx, sy;
|
|
|
|
// 0 not valid for water match tags
|
|
if (sectu->number == 0)
|
|
return FALSE;
|
|
|
|
// search for DIVE_AREA "over" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_DIVE_AREA], i, nexti)
|
|
{
|
|
over_sp = &sprite[i];
|
|
|
|
if (TEST(sector[over_sp->sectnum].extra, SECTFX_DIVE_AREA) &&
|
|
SectUser[over_sp->sectnum] &&
|
|
SectUser[over_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
Found = FALSE;
|
|
|
|
// search for UNDERWATER "under" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_UNDERWATER], i, nexti)
|
|
{
|
|
under_sp = &sprite[i];
|
|
|
|
if (TEST(sector[under_sp->sectnum].extra, SECTFX_UNDERWATER) &&
|
|
SectUser[under_sp->sectnum] &&
|
|
SectUser[under_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
|
|
// get the offset from the sprite
|
|
sx = over_sp->x - *x;
|
|
sy = over_sp->y - *y;
|
|
|
|
// update to the new x y position
|
|
*x = under_sp->x - sx;
|
|
*y = under_sp->y - sy;
|
|
|
|
over = over_sp->sectnum;
|
|
under = under_sp->sectnum;
|
|
|
|
if (GetOverlapSector(*x, *y, &over, &under) == 2)
|
|
{
|
|
*sectnum = under;
|
|
}
|
|
else
|
|
{
|
|
*sectnum = under;
|
|
}
|
|
|
|
*z = sector[under_sp->sectnum].ceilingz + Z(1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
SWBOOL
|
|
WarpToSurface(short *sectnum, int *x, int *y, int *z)
|
|
{
|
|
short i, nexti;
|
|
SECT_USERp sectu = SectUser[*sectnum];
|
|
short over, under;
|
|
int sx, sy;
|
|
|
|
SPRITEp under_sp = NULL, over_sp = NULL;
|
|
char Found = FALSE;
|
|
|
|
// 0 not valid for water match tags
|
|
if (sectu->number == 0)
|
|
return FALSE;
|
|
|
|
// search for UNDERWATER "under" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_UNDERWATER], i, nexti)
|
|
{
|
|
under_sp = &sprite[i];
|
|
|
|
if (TEST(sector[under_sp->sectnum].extra, SECTFX_UNDERWATER) &&
|
|
SectUser[under_sp->sectnum] &&
|
|
SectUser[under_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
Found = FALSE;
|
|
|
|
// search for DIVE_AREA "over" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_DIVE_AREA], i, nexti)
|
|
{
|
|
over_sp = &sprite[i];
|
|
|
|
if (TEST(sector[over_sp->sectnum].extra, SECTFX_DIVE_AREA) &&
|
|
SectUser[over_sp->sectnum] &&
|
|
SectUser[over_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
|
|
// get the offset from the under sprite
|
|
sx = under_sp->x - *x;
|
|
sy = under_sp->y - *y;
|
|
|
|
// update to the new x y position
|
|
*x = over_sp->x - sx;
|
|
*y = over_sp->y - sy;
|
|
|
|
over = over_sp->sectnum;
|
|
under = under_sp->sectnum;
|
|
|
|
if (GetOverlapSector(*x, *y, &over, &under))
|
|
{
|
|
*sectnum = over;
|
|
}
|
|
|
|
*z = sector[over_sp->sectnum].floorz - Z(2);
|
|
|
|
//MissileWaterAdjust(sp - sprite);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
SWBOOL
|
|
SpriteWarpToUnderwater(SPRITEp sp)
|
|
{
|
|
USERp u = User[sp - sprite];
|
|
short i, nexti;
|
|
SECT_USERp sectu = SectUser[sp->sectnum];
|
|
SPRITEp under_sp = NULL, over_sp = NULL;
|
|
char Found = FALSE;
|
|
short over, under;
|
|
int sx, sy;
|
|
|
|
// 0 not valid for water match tags
|
|
if (sectu->number == 0)
|
|
return FALSE;
|
|
|
|
// search for DIVE_AREA "over" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_DIVE_AREA], i, nexti)
|
|
{
|
|
over_sp = &sprite[i];
|
|
|
|
if (TEST(sector[over_sp->sectnum].extra, SECTFX_DIVE_AREA) &&
|
|
SectUser[over_sp->sectnum] &&
|
|
SectUser[over_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
Found = FALSE;
|
|
|
|
// search for UNDERWATER "under" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_UNDERWATER], i, nexti)
|
|
{
|
|
under_sp = &sprite[i];
|
|
|
|
if (TEST(sector[under_sp->sectnum].extra, SECTFX_UNDERWATER) &&
|
|
SectUser[under_sp->sectnum] &&
|
|
SectUser[under_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
|
|
// get the offset from the sprite
|
|
sx = over_sp->x - sp->x;
|
|
sy = over_sp->y - sp->y;
|
|
|
|
// update to the new x y position
|
|
sp->x = under_sp->x - sx;
|
|
sp->y = under_sp->y - sy;
|
|
|
|
over = over_sp->sectnum;
|
|
under = under_sp->sectnum;
|
|
|
|
if (GetOverlapSector(sp->x, sp->y, &over, &under) == 2)
|
|
{
|
|
changespritesect(sp - sprite, under);
|
|
}
|
|
else
|
|
{
|
|
changespritesect(sp - sprite, over);
|
|
}
|
|
|
|
//sp->z = sector[under_sp->sectnum].ceilingz + Z(6);
|
|
sp->z = sector[under_sp->sectnum].ceilingz + u->ceiling_dist+Z(1);
|
|
|
|
u->ox = sp->x;
|
|
u->oy = sp->y;
|
|
u->oz = sp->z;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
SWBOOL
|
|
SpriteWarpToSurface(SPRITEp sp)
|
|
{
|
|
USERp u = User[sp - sprite];
|
|
short i, nexti;
|
|
SECT_USERp sectu = SectUser[sp->sectnum];
|
|
short over, under;
|
|
int sx, sy;
|
|
|
|
SPRITEp under_sp = NULL, over_sp = NULL;
|
|
char Found = FALSE;
|
|
|
|
// 0 not valid for water match tags
|
|
if (sectu->number == 0)
|
|
return FALSE;
|
|
|
|
// search for UNDERWATER "under" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_UNDERWATER], i, nexti)
|
|
{
|
|
under_sp = &sprite[i];
|
|
|
|
if (TEST(sector[under_sp->sectnum].extra, SECTFX_UNDERWATER) &&
|
|
SectUser[under_sp->sectnum] &&
|
|
SectUser[under_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
|
|
if (under_sp->lotag == 0)
|
|
return FALSE;
|
|
|
|
Found = FALSE;
|
|
|
|
// search for DIVE_AREA "over" sprite for reference point
|
|
TRAVERSE_SPRITE_STAT(headspritestat[STAT_DIVE_AREA], i, nexti)
|
|
{
|
|
over_sp = &sprite[i];
|
|
|
|
if (TEST(sector[over_sp->sectnum].extra, SECTFX_DIVE_AREA) &&
|
|
SectUser[over_sp->sectnum] &&
|
|
SectUser[over_sp->sectnum]->number == sectu->number)
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT(Found == TRUE);
|
|
|
|
// get the offset from the under sprite
|
|
sx = under_sp->x - sp->x;
|
|
sy = under_sp->y - sp->y;
|
|
|
|
// update to the new x y position
|
|
sp->x = over_sp->x - sx;
|
|
sp->y = over_sp->y - sy;
|
|
|
|
over = over_sp->sectnum;
|
|
under = under_sp->sectnum;
|
|
|
|
if (GetOverlapSector(sp->x, sp->y, &over, &under))
|
|
{
|
|
changespritesect(sp - sprite, over);
|
|
}
|
|
|
|
sp->z = sector[over_sp->sectnum].floorz - Z(2);
|
|
|
|
// set z range and wade depth so we know how high to set view
|
|
DoActorZrange(sp - sprite);
|
|
MissileWaterAdjust(sp - sprite);
|
|
|
|
|
|
u->ox = sp->x;
|
|
u->oy = sp->y;
|
|
u->oz = sp->z;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
SpawnSplash(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum], wu;
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, wp;
|
|
short w;
|
|
|
|
SECT_USERp sectu = SectUser[sp->sectnum];
|
|
SECTORp sectp = §or[sp->sectnum];
|
|
|
|
if (Prediction)
|
|
return 0;
|
|
|
|
if (sectu && (TEST(sectp->extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_NONE))
|
|
return 0;
|
|
|
|
if (sectu && TEST(sectp->floorstat, FLOOR_STAT_PLAX))
|
|
return 0;
|
|
|
|
PlaySound(DIGI_SPLASH1, sp, v3df_none);
|
|
|
|
DoActorZrange(SpriteNum);
|
|
MissileWaterAdjust(SpriteNum);
|
|
|
|
w = SpawnSprite(STAT_MISSILE, SPLASH, s_Splash, sp->sectnum, sp->x, sp->y, u->loz, sp->ang, 0);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
if (sectu && TEST(sectp->extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
wu->spal = wp->pal = PALETTE_RED_LIGHTING;
|
|
|
|
wp->xrepeat = 45;
|
|
wp->yrepeat = 42;
|
|
wp->shade = sector[sp->sectnum].floorshade - 10;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnSplashXY(int hit_x, int hit_y, int hit_z, short sectnum)
|
|
{
|
|
USERp wu;
|
|
SPRITEp wp;
|
|
short w;
|
|
//short sectnum=0;
|
|
|
|
SECT_USERp sectu;
|
|
SECTORp sectp;
|
|
|
|
if (Prediction)
|
|
return 0;
|
|
|
|
sectu = SectUser[sectnum];
|
|
sectp = §or[sectnum];
|
|
|
|
if (sectu && (TEST(sectp->extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_NONE))
|
|
return 0;
|
|
|
|
if (sectu && TEST(sectp->floorstat, FLOOR_STAT_PLAX))
|
|
return 0;
|
|
|
|
w = SpawnSprite(STAT_MISSILE, SPLASH, s_Splash, sectnum, hit_x, hit_y, hit_z, 0, 0);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
if (sectu && TEST(sectp->extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
wu->spal = wp->pal = PALETTE_RED_LIGHTING;
|
|
|
|
wp->xrepeat = 45;
|
|
wp->yrepeat = 42;
|
|
wp->shade = sector[wp->sectnum].floorshade - 10;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpawnUnderSplash(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum], wu;
|
|
SPRITEp sp = User[SpriteNum]->SpriteP, wp;
|
|
short w;
|
|
|
|
SECT_USERp sectu = SectUser[sp->sectnum];
|
|
SECTORp sectp = §or[sp->sectnum];
|
|
|
|
return 0;
|
|
|
|
if (Prediction)
|
|
return 0;
|
|
|
|
if (sectu && (TEST(sectp->extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_NONE))
|
|
return 0;
|
|
|
|
DoActorZrange(SpriteNum);
|
|
MissileWaterAdjust(SpriteNum);
|
|
|
|
w = SpawnSprite(STAT_MISSILE, SPLASH, s_Splash, sp->sectnum, sp->x, sp->y, u->hiz, sp->ang, 0);
|
|
wp = &sprite[w];
|
|
wu = User[w];
|
|
|
|
if (sectu && TEST(sectp->extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
wu->spal = wp->pal = PALETTE_RED_LIGHTING;
|
|
|
|
wp->xrepeat = 72;
|
|
wp->yrepeat = 90;
|
|
wp->shade = sector[sp->sectnum].floorshade - 10;
|
|
SET(wp->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SWBOOL
|
|
MissileHitDiveArea(short SpriteNum)
|
|
{
|
|
USERp u = User[SpriteNum];
|
|
SPRITEp sp = User[SpriteNum]->SpriteP;
|
|
|
|
// correctly set underwater bit for missiles
|
|
// in Stacked water areas.
|
|
if (FAF_ConnectArea(sp->sectnum))
|
|
{
|
|
if (SectorIsUnderwaterArea(sp->sectnum))
|
|
SET(u->Flags, SPR_UNDERWATER);
|
|
else
|
|
RESET(u->Flags, SPR_UNDERWATER);
|
|
}
|
|
|
|
if (!u->ret)
|
|
return FALSE;
|
|
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SECTOR:
|
|
{
|
|
short hit_sect = NORM_SECTOR(u->ret);
|
|
|
|
if (SpriteInDiveArea(sp))
|
|
{
|
|
// make sure you are close to the floor
|
|
if (sp->z < DIV2(u->hiz + u->loz))
|
|
return FALSE;
|
|
|
|
// Check added by Jim because of sprite bridge over water
|
|
if (sp->z < (sector[hit_sect].floorz-Z(20)))
|
|
return FALSE;
|
|
|
|
SET(u->Flags, SPR_UNDERWATER);
|
|
SpawnSplash(sp - sprite);
|
|
SpriteWarpToUnderwater(sp);
|
|
//SpawnUnderSplash(sp - sprite);
|
|
u->ret = 0;
|
|
PlaySound(DIGI_PROJECTILEWATERHIT, sp, v3df_none);
|
|
return TRUE;
|
|
}
|
|
else if (SpriteInUnderwaterArea(sp))
|
|
{
|
|
// make sure you are close to the ceiling
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
return FALSE;
|
|
|
|
RESET(u->Flags, SPR_UNDERWATER);
|
|
if (!SpriteWarpToSurface(sp))
|
|
{
|
|
return FALSE;
|
|
}
|
|
SpawnSplash(sp - sprite);
|
|
u->ret = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
SpawnBubble(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum], bp;
|
|
USERp bu;
|
|
short b;
|
|
|
|
if (Prediction)
|
|
return -1;
|
|
|
|
b = SpawnSprite(STAT_MISSILE, BUBBLE, s_Bubble, sp->sectnum, sp->x, sp->y, sp->z, sp->ang, 0);
|
|
bp = &sprite[b];
|
|
bu = User[b];
|
|
|
|
//PlaySound(DIGI_BUBBLES, sp, v3df_none);
|
|
|
|
bp->xrepeat = 8 + (RANDOM_P2(8 << 8) >> 8);
|
|
bp->yrepeat = bp->xrepeat;
|
|
bu->sx = bp->xrepeat;
|
|
bu->sy = bp->yrepeat;
|
|
bu->ceiling_dist = Z(1);
|
|
bu->floor_dist = Z(1);
|
|
bp->shade = sector[sp->sectnum].floorshade - 10;
|
|
bu->WaitTics = 120 * 120;
|
|
bp->zvel = 512;
|
|
bp->clipdist = 12 >> 2;
|
|
SET(bp->cstat, CSTAT_SPRITE_YCENTER);
|
|
SET(bu->Flags, SPR_UNDERWATER);
|
|
bp->shade = -60; // Make em brighter
|
|
|
|
return b;
|
|
}
|
|
|
|
int
|
|
DoVehicleSmoke(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
sp->z -= sp->zvel;
|
|
|
|
sp->x += u->xchange;
|
|
sp->y += u->ychange;
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
DoWaterSmoke(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
sp->z -= sp->zvel;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
SpawnVehicleSmoke(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum],np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
if (MoveSkip2 != 0)
|
|
return FALSE;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, s_VehicleSmoke, sp->sectnum,
|
|
sp->x, sp->y, sp->z - RANDOM_P2(Z(8)), sp->ang, 0);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
nu->WaitTics = 1*120;
|
|
np->shade = -40;
|
|
np->xrepeat = 64;
|
|
np->yrepeat = 64;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
if (RANDOM_P2(1024) < 512)
|
|
SET(np->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024) < 512)
|
|
SET(np->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
np->ang = RANDOM_P2(2048);
|
|
np->xvel = RANDOM_P2(32);
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
np->zvel = Z(4) + RANDOM_P2(Z(4));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
SpawnSmokePuff(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum],np;
|
|
USERp nu;
|
|
short New;
|
|
|
|
New = SpawnSprite(STAT_MISSILE, PUFF, s_WaterSmoke, sp->sectnum,
|
|
sp->x, sp->y, sp->z - RANDOM_P2(Z(8)), sp->ang, 0);
|
|
|
|
np = &sprite[New];
|
|
nu = User[New];
|
|
|
|
nu->WaitTics = 1*120;
|
|
np->shade = -40;
|
|
np->xrepeat = 64;
|
|
np->yrepeat = 64;
|
|
SET(np->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
if (RANDOM_P2(1024) < 512)
|
|
SET(np->cstat, CSTAT_SPRITE_XFLIP);
|
|
if (RANDOM_P2(1024) < 512)
|
|
SET(np->cstat, CSTAT_SPRITE_YFLIP);
|
|
|
|
np->ang = RANDOM_P2(2048);
|
|
np->xvel = RANDOM_P2(32);
|
|
nu->xchange = MOVEx(np->xvel, np->ang);
|
|
nu->ychange = MOVEy(np->xvel, np->ang);
|
|
//np->zvel = Z(4) + RANDOM_P2(Z(4));
|
|
np->zvel = Z(1) + RANDOM_P2(Z(2));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
DoBubble(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
sp->z -= sp->zvel;
|
|
sp->zvel += 32;
|
|
|
|
if (sp->zvel > 768)
|
|
sp->zvel = 768;
|
|
|
|
u->sx += 1;
|
|
u->sy += 1;
|
|
|
|
if (u->sx > 32)
|
|
{
|
|
u->sx = 32;
|
|
u->sy = 32;
|
|
}
|
|
|
|
sp->xrepeat = u->sx + (RANDOM_P2(8 << 8) >> 8) - 4;
|
|
sp->yrepeat = u->sy + (RANDOM_P2(8 << 8) >> 8) - 4;
|
|
|
|
if (sp->z < sector[sp->sectnum].ceilingz)
|
|
{
|
|
if (SectorIsUnderwaterArea(u->hi_sectp - sector))
|
|
{
|
|
if (!SpriteWarpToSurface(sp))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
|
|
RESET(u->Flags, SPR_UNDERWATER);
|
|
// stick around above water for this long
|
|
u->WaitTics = (RANDOM_P2(64 << 8) >> 8);
|
|
}
|
|
else
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
if ((u->WaitTics -= MISSILEMOVETICS) <= 0)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
// just in case its stuck somewhere kill it after a while
|
|
{
|
|
if ((u->WaitTics -= MISSILEMOVETICS) <= 0)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// this needs to be called before killsprite
|
|
// whenever killing a sprite that you aren't completely sure what it is, like
|
|
// with the drivables, copy sectors, break sprites, etc
|
|
void
|
|
SpriteQueueDelete(short SpriteNum)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_STAR_QUEUE; i++)
|
|
if (StarQueue[i] == SpriteNum)
|
|
StarQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_HOLE_QUEUE; i++)
|
|
if (HoleQueue[i] == SpriteNum)
|
|
HoleQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_WALLBLOOD_QUEUE; i++)
|
|
if (WallBloodQueue[i] == SpriteNum)
|
|
WallBloodQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_FLOORBLOOD_QUEUE; i++)
|
|
if (FloorBloodQueue[i] == SpriteNum)
|
|
FloorBloodQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_GENERIC_QUEUE; i++)
|
|
if (GenericQueue[i] == SpriteNum)
|
|
GenericQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_LOWANGS_QUEUE; i++)
|
|
if (LoWangsQueue[i] == SpriteNum)
|
|
LoWangsQueue[i] = -1;
|
|
}
|
|
|
|
|
|
void QueueReset(void)
|
|
{
|
|
short i;
|
|
StarQueueHead=0;
|
|
HoleQueueHead=0;
|
|
WallBloodQueueHead=0;
|
|
FloorBloodQueueHead=0;
|
|
GenericQueueHead=0;
|
|
LoWangsQueueHead=0;
|
|
|
|
|
|
for (i = 0; i < MAX_STAR_QUEUE; i++)
|
|
StarQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_HOLE_QUEUE; i++)
|
|
HoleQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_WALLBLOOD_QUEUE; i++)
|
|
WallBloodQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_FLOORBLOOD_QUEUE; i++)
|
|
FloorBloodQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_GENERIC_QUEUE; i++)
|
|
GenericQueue[i] = -1;
|
|
|
|
for (i = 0; i < MAX_LOWANGS_QUEUE; i++)
|
|
LoWangsQueue[i] = -1;
|
|
}
|
|
|
|
SWBOOL TestDontStick(short SpriteNum, short hit_wall)
|
|
{
|
|
WALLp wp;
|
|
|
|
if (hit_wall < 0)
|
|
{
|
|
ASSERT(SpriteNum>=0);
|
|
USERp u = User[SpriteNum];
|
|
hit_wall = NORM_WALL(u->ret);
|
|
}
|
|
|
|
wp = &wall[hit_wall];
|
|
|
|
if (TEST(wp->extra, WALLFX_DONT_STICK))
|
|
return TRUE;
|
|
|
|
// if blocking red wallo
|
|
if (TEST(wp->cstat, CSTAT_WALL_BLOCK) && (uint16_t)wp->nextwall < MAXWALLS)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SWBOOL TestDontStickSector(short hit_sect)
|
|
{
|
|
if (TEST(sector[hit_sect].extra, SECTFX_DYNAMIC_AREA|SECTFX_SECTOR_OBJECT))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int QueueStar(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
SPRITEp osp;
|
|
|
|
if (TestDontStick(SpriteNum, -1))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return -1;
|
|
}
|
|
|
|
// can and should kill the user portion of the star
|
|
if (StarQueue[StarQueueHead] == -1)
|
|
{
|
|
// new star
|
|
if (User[SpriteNum])
|
|
{
|
|
FreeMem(User[SpriteNum]);
|
|
User[SpriteNum] = NULL;
|
|
}
|
|
change_sprite_stat(SpriteNum, STAT_STAR_QUEUE);
|
|
StarQueue[StarQueueHead] = SpriteNum;
|
|
}
|
|
else
|
|
{
|
|
// move old star to new stars place
|
|
osp = &sprite[StarQueue[StarQueueHead]];
|
|
osp->x = sp->x;
|
|
osp->y = sp->y;
|
|
osp->z = sp->z;
|
|
changespritesect(StarQueue[StarQueueHead], sp->sectnum);
|
|
KillSprite(SpriteNum);
|
|
SpriteNum = StarQueue[StarQueueHead];
|
|
ASSERT(sprite[SpriteNum].statnum != MAXSTATUS);
|
|
}
|
|
|
|
StarQueueHead = (StarQueueHead+1) & (MAX_STAR_QUEUE-1);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
int QueueHole(short hit_sect, short hit_wall, int hit_x, int hit_y, int hit_z)
|
|
{
|
|
short w,nw,wall_ang;
|
|
short SpriteNum;
|
|
int nx,ny;
|
|
SPRITEp sp;
|
|
short sectnum;
|
|
|
|
|
|
if (TestDontStick(-1,hit_wall))
|
|
return -1;
|
|
|
|
if (HoleQueue[HoleQueueHead] == -1)
|
|
HoleQueue[HoleQueueHead] = SpriteNum = COVERinsertsprite(hit_sect, STAT_HOLE_QUEUE);
|
|
else
|
|
SpriteNum = HoleQueue[HoleQueueHead];
|
|
|
|
HoleQueueHead = (HoleQueueHead+1) & (MAX_HOLE_QUEUE-1);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
sp->owner = -1;
|
|
sp->xrepeat = sp->yrepeat = 16;
|
|
sp->cstat = sp->pal = sp->shade = sp->extra = sp->clipdist = sp->xoffset = sp->yoffset = 0;
|
|
sp->x = hit_x;
|
|
sp->y = hit_y;
|
|
sp->z = hit_z;
|
|
sp->picnum = 2151;
|
|
changespritesect(SpriteNum, hit_sect);
|
|
|
|
ASSERT(sp->statnum != MAXSTATUS);
|
|
|
|
SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
SET(sp->cstat, CSTAT_SPRITE_ONE_SIDED);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
w = hit_wall;
|
|
nw = wall[w].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wall[w].x, wall[nw].y - wall[w].y)+512);
|
|
sp->ang = wall_ang;
|
|
|
|
// move it back some
|
|
nx = sintable[(512 + sp->ang) & 2047]<<4;
|
|
ny = sintable[sp->ang]<<4;
|
|
|
|
sectnum = sp->sectnum;
|
|
|
|
clipmoveboxtracenum = 1;
|
|
clipmove((vec3_t *)sp, §num, nx, ny,
|
|
0L, 0L, 0L, CLIPMASK_MISSILE);
|
|
clipmoveboxtracenum = 3;
|
|
|
|
if (sp->sectnum != sectnum)
|
|
changespritesect(SpriteNum, sectnum);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
#define FLOORBLOOD_RATE 30
|
|
ANIMATOR DoFloorBlood;
|
|
STATE s_FloorBlood1[] =
|
|
{
|
|
{FLOORBLOOD1, SF_QUICK_CALL, DoFloorBlood, &s_FloorBlood1[1]},
|
|
{FLOORBLOOD1, FLOORBLOOD_RATE, NullAnimator, &s_FloorBlood1[0]},
|
|
};
|
|
|
|
int QueueFloorBlood(short hit_sprite)
|
|
{
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
USERp u = User[hit_sprite];
|
|
SECTORp sectp = §or[hsp->sectnum];
|
|
|
|
|
|
if (TEST(sectp->extra, SECTFX_SINK)||TEST(sectp->extra, SECTFX_CURRENT))
|
|
return -1; // No blood in water or current areas
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) || SpriteInUnderwaterArea(hsp) || SpriteInDiveArea(hsp))
|
|
return -1; // No blood underwater!
|
|
|
|
if (TEST(sector[hsp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER)
|
|
return -1; // No prints liquid areas!
|
|
|
|
if (TEST(sector[hsp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
return -1; // Not in lave either
|
|
|
|
if (TestDontStickSector(hsp->sectnum))
|
|
return -1; // Not on special sectors you don't
|
|
|
|
if (FloorBloodQueue[FloorBloodQueueHead] != -1)
|
|
KillSprite(FloorBloodQueue[FloorBloodQueueHead]);
|
|
|
|
FloorBloodQueue[FloorBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_SKIP4, FLOORBLOOD1, s_FloorBlood1, hsp->sectnum, hsp->x, hsp->y, hsp->z, hsp->ang, 0);
|
|
|
|
FloorBloodQueueHead = (FloorBloodQueueHead+1) & (MAX_FLOORBLOOD_QUEUE-1);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
sp->owner = -1;
|
|
// Stupid hack to fix the blood under the skull to not show through
|
|
// x,y repeat of floor blood MUST be smaller than the sprite above it or clipping probs.
|
|
if (u->ID == GORE_Head)
|
|
sp->hitag = 9995;
|
|
else
|
|
sp->hitag = 0;
|
|
sp->xrepeat = sp->yrepeat = 8;
|
|
sp->cstat = sp->pal = sp->shade = sp->extra = sp->clipdist = sp->xoffset = sp->yoffset = 0;
|
|
sp->x = hsp->x;
|
|
sp->y = hsp->y;
|
|
sp->z = hsp->z + Z(1);
|
|
sp->ang = RANDOM_P2(2048); // Just make it any old angle
|
|
sp->shade -= 5; // Brighten it up just a bit
|
|
|
|
SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR);
|
|
SET(sp->cstat, CSTAT_SPRITE_ONE_SIDED);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
RESET(u->Flags, SPR_SHADOW);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
#define FOOTPRINT1 2490
|
|
#define FOOTPRINT2 2491
|
|
#define FOOTPRINT3 2492
|
|
#define FOOTPRINT_RATE 30
|
|
ANIMATOR DoFootPrint;
|
|
STATE s_FootPrint1[] =
|
|
{
|
|
{FOOTPRINT1, FOOTPRINT_RATE, NullAnimator, &s_FootPrint1[0]},
|
|
};
|
|
STATE s_FootPrint2[] =
|
|
{
|
|
{FOOTPRINT2, FOOTPRINT_RATE, NullAnimator, &s_FootPrint2[0]},
|
|
};
|
|
STATE s_FootPrint3[] =
|
|
{
|
|
{FOOTPRINT3, FOOTPRINT_RATE, NullAnimator, &s_FootPrint3[0]},
|
|
};
|
|
|
|
int QueueFootPrint(short hit_sprite)
|
|
{
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
short SpriteNum;
|
|
SPRITEp sp;
|
|
USERp u = User[hit_sprite];
|
|
USERp nu;
|
|
short rnd_num=0;
|
|
SWBOOL Found=FALSE;
|
|
SECTORp sectp = §or[hsp->sectnum];
|
|
|
|
|
|
if (TEST(sectp->extra, SECTFX_SINK)||TEST(sectp->extra, SECTFX_CURRENT))
|
|
return -1; // No blood in water or current areas
|
|
|
|
if (u->PlayerP)
|
|
{
|
|
if (TEST(u->PlayerP->Flags, PF_DIVING))
|
|
Found = TRUE;
|
|
|
|
// Stupid masked floor stuff! Damn your weirdness!
|
|
if (TEST(sector[u->PlayerP->cursectnum].ceilingstat, CEILING_STAT_PLAX))
|
|
Found = TRUE;
|
|
if (TEST(sector[u->PlayerP->cursectnum].floorstat, CEILING_STAT_PLAX))
|
|
Found = TRUE;
|
|
}
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) || SpriteInUnderwaterArea(hsp) || Found || SpriteInDiveArea(hsp))
|
|
return -1; // No prints underwater!
|
|
|
|
if (TEST(sector[hsp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER)
|
|
return -1; // No prints liquid areas!
|
|
|
|
if (TEST(sector[hsp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
return -1; // Not in lave either
|
|
|
|
if (TestDontStickSector(hsp->sectnum))
|
|
return -1; // Not on special sectors you don't
|
|
|
|
// So, are we like, done checking now!?
|
|
if (FloorBloodQueue[FloorBloodQueueHead] != -1)
|
|
KillSprite(FloorBloodQueue[FloorBloodQueueHead]);
|
|
|
|
rnd_num = RANDOM_RANGE(1024);
|
|
|
|
if (rnd_num > 683)
|
|
FloorBloodQueue[FloorBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, FOOTPRINT1, s_FootPrint1, hsp->sectnum, hsp->x, hsp->y, hsp->z, hsp->ang, 0);
|
|
else if (rnd_num > 342)
|
|
FloorBloodQueue[FloorBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, FOOTPRINT2, s_FootPrint2, hsp->sectnum, hsp->x, hsp->y, hsp->z, hsp->ang, 0);
|
|
else
|
|
FloorBloodQueue[FloorBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, FOOTPRINT3, s_FootPrint3, hsp->sectnum, hsp->x, hsp->y, hsp->z, hsp->ang, 0);
|
|
|
|
FloorBloodQueueHead = (FloorBloodQueueHead+1) & (MAX_FLOORBLOOD_QUEUE-1);
|
|
|
|
// Decrease footprint count
|
|
if (u->PlayerP)
|
|
u->PlayerP->NumFootPrints--;
|
|
|
|
|
|
sp = &sprite[SpriteNum];
|
|
nu = User[SpriteNum];
|
|
sp->hitag = 0;
|
|
sp->owner = -1;
|
|
sp->xrepeat = 48;
|
|
sp->yrepeat = 54;
|
|
sp->cstat = sp->pal = sp->shade = sp->extra = sp->clipdist = sp->xoffset = sp->yoffset = 0;
|
|
sp->x = hsp->x;
|
|
sp->y = hsp->y;
|
|
sp->z = hsp->z;
|
|
sp->ang = hsp->ang;
|
|
RESET(nu->Flags, SPR_SHADOW);
|
|
switch (FootMode)
|
|
{
|
|
case BLOOD_FOOT:
|
|
nu->spal = sp->pal = PALETTE_PLAYER3; // Turn blue to blood red
|
|
break;
|
|
default:
|
|
nu->spal = sp->pal = PALETTE_PLAYER1; // Gray water
|
|
break;
|
|
}
|
|
|
|
|
|
// Alternate the feet
|
|
left_foot = !left_foot;
|
|
if (left_foot)
|
|
SET(sp->cstat, CSTAT_SPRITE_XFLIP);
|
|
SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR);
|
|
SET(sp->cstat, CSTAT_SPRITE_ONE_SIDED);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
#define WALLBLOOD1 2500
|
|
#define WALLBLOOD2 2501
|
|
#define WALLBLOOD3 2502
|
|
#define WALLBLOOD4 2503
|
|
#define WALLBLOOD_RATE 30
|
|
ANIMATOR DoWallBlood;
|
|
STATE s_WallBlood1[] =
|
|
{
|
|
{WALLBLOOD1, SF_QUICK_CALL, DoWallBlood, &s_WallBlood1[1]},
|
|
{WALLBLOOD1, WALLBLOOD_RATE, NullAnimator, &s_WallBlood1[0]},
|
|
};
|
|
STATE s_WallBlood2[] =
|
|
{
|
|
{WALLBLOOD2, SF_QUICK_CALL, DoWallBlood, &s_WallBlood2[1]},
|
|
{WALLBLOOD2, WALLBLOOD_RATE, NullAnimator, &s_WallBlood2[0]},
|
|
};
|
|
STATE s_WallBlood3[] =
|
|
{
|
|
{WALLBLOOD3, SF_QUICK_CALL, DoWallBlood, &s_WallBlood3[1]},
|
|
{WALLBLOOD3, WALLBLOOD_RATE, NullAnimator, &s_WallBlood3[0]},
|
|
};
|
|
STATE s_WallBlood4[] =
|
|
{
|
|
{WALLBLOOD4, SF_QUICK_CALL, DoWallBlood, &s_WallBlood4[1]},
|
|
{WALLBLOOD4, WALLBLOOD_RATE, NullAnimator, &s_WallBlood4[0]},
|
|
};
|
|
|
|
|
|
int QueueWallBlood(short hit_sprite, short ang)
|
|
{
|
|
SPRITEp hsp = &sprite[hit_sprite];
|
|
short w,nw,wall_ang,dang;
|
|
short SpriteNum;
|
|
int nx,ny;
|
|
SPRITEp sp;
|
|
short sectnum;
|
|
short rndnum;
|
|
int daz;
|
|
hitdata_t hitinfo;
|
|
USERp u = User[hit_sprite];
|
|
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) || SpriteInUnderwaterArea(hsp) || SpriteInDiveArea(hsp))
|
|
return -1; // No blood underwater!
|
|
|
|
daz = Z(RANDOM_P2(128))<<3;
|
|
daz -= DIV2(Z(128)<<3);
|
|
dang = (ang+(RANDOM_P2(128<<5) >> 5)) - DIV2(128);
|
|
|
|
FAFhitscan(hsp->x, hsp->y, hsp->z - Z(30), hsp->sectnum, // Start position
|
|
sintable[NORM_ANGLE(dang + 512)], // X vector of 3D ang
|
|
sintable[NORM_ANGLE(dang)], // Y vector of 3D ang
|
|
daz, // Z vector of 3D ang
|
|
&hitinfo, CLIPMASK_MISSILE);
|
|
|
|
if (hitinfo.sect < 0)
|
|
return -1;
|
|
|
|
#define WALLBLOOD_DIST_MAX 2500
|
|
if (Distance(hitinfo.pos.x, hitinfo.pos.y, hsp->x, hsp->y) > WALLBLOOD_DIST_MAX)
|
|
return -1;
|
|
|
|
// hit a sprite?
|
|
if (hitinfo.sprite >= 0)
|
|
return 0; // Don't try to put blood on a sprite
|
|
|
|
if (hitinfo.wall >= 0) // Don't check if blood didn't hit a wall, otherwise the ASSERT fails!
|
|
{
|
|
if (TestDontStick(-1, hitinfo.wall))
|
|
return -1;
|
|
}
|
|
else
|
|
return -1;
|
|
|
|
|
|
if (WallBloodQueue[WallBloodQueueHead] != -1)
|
|
KillSprite(WallBloodQueue[WallBloodQueueHead]);
|
|
|
|
// Randomly choose a wall blood sprite
|
|
rndnum = RANDOM_RANGE(1024);
|
|
if (rndnum > 768)
|
|
{
|
|
WallBloodQueue[WallBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, WALLBLOOD1, s_WallBlood1, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang, 0);
|
|
}
|
|
else if (rndnum > 512)
|
|
{
|
|
WallBloodQueue[WallBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, WALLBLOOD2, s_WallBlood2, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang, 0);
|
|
}
|
|
else if (rndnum > 128)
|
|
{
|
|
WallBloodQueue[WallBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, WALLBLOOD3, s_WallBlood3, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang, 0);
|
|
}
|
|
else
|
|
{
|
|
WallBloodQueue[WallBloodQueueHead] = SpriteNum =
|
|
SpawnSprite(STAT_WALLBLOOD_QUEUE, WALLBLOOD4, s_WallBlood4, hitinfo.sect, hitinfo.pos.x, hitinfo.pos.y, hitinfo.pos.z, ang, 0);
|
|
}
|
|
|
|
WallBloodQueueHead = (WallBloodQueueHead+1) & (MAX_WALLBLOOD_QUEUE-1);
|
|
|
|
sp = &sprite[SpriteNum];
|
|
sp->owner = -1;
|
|
sp->xrepeat = 30;
|
|
sp->yrepeat = 40; // yrepeat will grow towards 64, it's default size
|
|
sp->cstat = sp->pal = sp->shade = sp->extra = sp->clipdist = sp->xoffset = sp->yoffset = 0;
|
|
sp->x = hitinfo.pos.x;
|
|
sp->y = hitinfo.pos.y;
|
|
sp->z = hitinfo.pos.z;
|
|
sp->shade -= 5; // Brighten it up just a bit
|
|
sp->yvel = hitinfo.wall; // pass hitinfo.wall in yvel
|
|
|
|
SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
SET(sp->cstat, CSTAT_SPRITE_ONE_SIDED);
|
|
SET(sp->cstat, CSTAT_SPRITE_YCENTER);
|
|
RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
w = hitinfo.wall;
|
|
nw = wall[w].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wall[w].x, wall[nw].y - wall[w].y)+512);
|
|
sp->ang = wall_ang;
|
|
|
|
// move it back some
|
|
nx = sintable[(512 + sp->ang) & 2047]<<4;
|
|
ny = sintable[sp->ang]<<4;
|
|
|
|
sectnum = sp->sectnum;
|
|
|
|
clipmoveboxtracenum = 1;
|
|
clipmove((vec3_t *)sp, §num, nx, ny,
|
|
0L, 0L, 0L, CLIPMASK_MISSILE);
|
|
clipmoveboxtracenum = 3;
|
|
|
|
if (sp->sectnum != sectnum)
|
|
changespritesect(SpriteNum, sectnum);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
#define FEET_IN_BLOOD_DIST 300
|
|
int
|
|
DoFloorBlood(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
SPRITEp psp = User[SpriteNum]->SpriteP;
|
|
int dist, near_dist = FEET_IN_BLOOD_DIST, a,b,c;
|
|
short pnum;
|
|
PLAYERp pp;
|
|
short xsiz,ysiz;
|
|
|
|
|
|
if (sp->hitag == 9995)
|
|
{
|
|
xsiz = 12;
|
|
ysiz = 12;
|
|
}
|
|
else
|
|
{
|
|
xsiz = 40;
|
|
ysiz = 40;
|
|
}
|
|
|
|
// Make pool of blood seem to grow
|
|
if (sp->xrepeat < xsiz && sp->xrepeat != 4)
|
|
{
|
|
sp->xrepeat++;
|
|
}
|
|
|
|
if (sp->yrepeat < ysiz && sp->xrepeat != xsiz && sp->xrepeat != 4)
|
|
{
|
|
sp->yrepeat++;
|
|
}
|
|
|
|
// See if any players stepped in blood
|
|
if (sp->xrepeat != 4 && sp->yrepeat > 4)
|
|
{
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
pp = &Player[pnum];
|
|
|
|
DISTANCE(psp->x, psp->y, pp->posx, pp->posy, dist, a, b, c);
|
|
|
|
// //DSPRINTF(ds,"dist = %ld\n",dist);
|
|
// MONO_PRINT(ds);
|
|
|
|
if (dist < near_dist)
|
|
{
|
|
if (pp->NumFootPrints <= 0 || FootMode != BLOOD_FOOT)
|
|
{
|
|
pp->NumFootPrints = RANDOM_RANGE(10)+3;
|
|
FootMode = BLOOD_FOOT;
|
|
}
|
|
|
|
// If blood has already grown to max size, we can shrink it
|
|
if (sp->xrepeat == 40 && sp->yrepeat > 10)
|
|
{
|
|
sp->yrepeat -= 10;
|
|
if (sp->yrepeat <= 10) // Shrink it down and don't use it anymore
|
|
sp->xrepeat = sp->yrepeat = 4;
|
|
}
|
|
// //DSPRINTF(ds,"pp->NumFootPrints = %d\n",pp->NumFootPrints);
|
|
// MONO_PRINT(ds);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
DoWallBlood(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
|
|
// Make blood drip down the wall
|
|
if (sp->yrepeat < 80)
|
|
{
|
|
sp->yrepeat++;
|
|
sp->z += 128;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This is the FAST queue, it doesn't call any animator functions or states
|
|
int QueueGeneric(short SpriteNum, short pic)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
SPRITEp osp;
|
|
short xrepeat,yrepeat;
|
|
|
|
if (TEST(sector[sp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return -1;
|
|
}
|
|
|
|
if (TEST(sector[sp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return -1;
|
|
}
|
|
|
|
if (TestDontStickSector(sp->sectnum))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return -1;
|
|
}
|
|
|
|
xrepeat = sp->xrepeat;
|
|
yrepeat = sp->yrepeat;
|
|
|
|
// can and should kill the user portion
|
|
if (GenericQueue[GenericQueueHead] == -1)
|
|
{
|
|
if (User[SpriteNum])
|
|
{
|
|
FreeMem(User[SpriteNum]);
|
|
User[SpriteNum] = NULL;
|
|
}
|
|
change_sprite_stat(SpriteNum, STAT_GENERIC_QUEUE);
|
|
GenericQueue[GenericQueueHead] = SpriteNum;
|
|
}
|
|
else
|
|
{
|
|
// move old sprite to new sprite's place
|
|
osp = &sprite[GenericQueue[GenericQueueHead]];
|
|
//setspritez(GenericQueue[GenericQueueHead], (vec3_t *)sp);
|
|
osp->x = sp->x;
|
|
osp->y = sp->y;
|
|
osp->z = sp->z;
|
|
changespritesect(GenericQueue[GenericQueueHead], sp->sectnum);
|
|
KillSprite(SpriteNum);
|
|
SpriteNum = GenericQueue[GenericQueueHead];
|
|
ASSERT(sprite[SpriteNum].statnum != MAXSTATUS);
|
|
}
|
|
|
|
sp = &sprite[SpriteNum];
|
|
ASSERT(sp);
|
|
sp->picnum = pic;
|
|
sp->xrepeat = xrepeat;
|
|
sp->yrepeat = yrepeat;
|
|
sp->cstat = 0;
|
|
switch (sp->picnum)
|
|
{
|
|
case 900:
|
|
case 901:
|
|
case 902:
|
|
case 915:
|
|
case 916:
|
|
case 917:
|
|
case 930:
|
|
case 931:
|
|
case 932:
|
|
case GORE_Head:
|
|
change_sprite_stat(SpriteNum,STAT_DEFAULT); // Breakable
|
|
SET(sp->cstat, CSTAT_SPRITE_BREAKABLE);
|
|
SET(sp->extra, SPRX_BREAKABLE);
|
|
break;
|
|
default:
|
|
RESET(sp->cstat, CSTAT_SPRITE_BREAKABLE);
|
|
RESET(sp->extra, SPRX_BREAKABLE);
|
|
break;
|
|
}
|
|
|
|
GenericQueueHead = (GenericQueueHead+1) & (MAX_GENERIC_QUEUE-1);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
DoShellShrap(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
// If the shell doesn't fall in the allowable range, kill it.
|
|
if (u->ShellNum < (ShellCount-MAXSHELLS))
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
// Get rid of shell if they fall in non-divable liquid areas
|
|
if (TEST(sector[sp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
if (TEST(sector[sp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
SpawnShell(short SpriteNum, short ShellNum)
|
|
{
|
|
extern int InitShell(int16_t SpriteNum, int16_t ShellNum);
|
|
|
|
//ShellCount++;
|
|
//SpawnShrap(SpriteNum,ShellNum); // -2 signifies right Uzi shell
|
|
// -3 is left Uzi
|
|
// -4 is Shotgun shell
|
|
|
|
InitShell(SpriteNum, ShellNum);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
DoShrapVelocity(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER) || SpriteInUnderwaterArea(sp))
|
|
{
|
|
ScaleSpriteVector(SpriteNum, 20000);
|
|
|
|
u->Counter += 8*4; // These are MoveSkip4 now
|
|
u->zchange += u->Counter;
|
|
}
|
|
else
|
|
{
|
|
u->Counter += 60*4;
|
|
u->zchange += u->Counter;
|
|
}
|
|
|
|
u->ret = move_missile(SpriteNum, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2);
|
|
|
|
MissileHitDiveArea(SpriteNum);
|
|
|
|
if (u->ret)
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_PLAX_WALL:
|
|
KillSprite(SpriteNum);
|
|
return TRUE;
|
|
case HIT_SPRITE:
|
|
{
|
|
short wall_ang;
|
|
short hit_sprite = -2;
|
|
SPRITEp hsp;
|
|
// PlaySound(DIGI_DHCLUNK, sp, v3df_dontpan);
|
|
|
|
hit_sprite = NORM_SPRITE(u->ret);
|
|
hsp = &sprite[hit_sprite];
|
|
|
|
wall_ang = NORM_ANGLE(hsp->ang);
|
|
WallBounce(SpriteNum, wall_ang);
|
|
ScaleSpriteVector(SpriteNum, 32000);
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall,nw,wall_ang;
|
|
WALLp wph;
|
|
|
|
|
|
hit_wall = NORM_WALL(u->ret);
|
|
wph = &wall[hit_wall];
|
|
|
|
|
|
// PlaySound(DIGI_DHCLUNK, sp, v3df_dontpan);
|
|
|
|
nw = wall[hit_wall].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y)+512);
|
|
|
|
WallBounce(SpriteNum, wall_ang);
|
|
ScaleSpriteVector(SpriteNum, 32000);
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
SWBOOL did_hit_wall;
|
|
|
|
if (SlopeBounce(SpriteNum, &did_hit_wall))
|
|
{
|
|
if (did_hit_wall)
|
|
{
|
|
// hit a wall
|
|
ScaleSpriteVector(SpriteNum, 28000);
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
}
|
|
else
|
|
{
|
|
// hit a sector
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
// hit a floor
|
|
if (!TEST(u->Flags, SPR_BOUNCE))
|
|
{
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
ScaleSpriteVector(SpriteNum, 18000);
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
}
|
|
else
|
|
{
|
|
if (u->ID == GORE_Drip)
|
|
ChangeState(SpriteNum, s_GoreFloorSplash);
|
|
else
|
|
ShrapKillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hit a ceiling
|
|
ScaleSpriteVector(SpriteNum, 22000);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hit floor
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
sp->z = u->loz;
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
SET(u->Flags, SPR_BOUNCE); // no bouncing underwater
|
|
|
|
if (u->lo_sectp && SectUser[sp->sectnum] && SectUser[sp->sectnum]->depth)
|
|
SET(u->Flags, SPR_BOUNCE); // no bouncing on shallow water
|
|
|
|
if (!TEST(u->Flags, SPR_BOUNCE))
|
|
{
|
|
SET(u->Flags, SPR_BOUNCE);
|
|
u->ret = 0;
|
|
u->Counter = 0;
|
|
u->zchange = -u->zchange;
|
|
ScaleSpriteVector(SpriteNum, 18000);
|
|
switch (u->ID)
|
|
{
|
|
case UZI_SHELL:
|
|
PlaySound(DIGI_SHELL, sp, v3df_none);
|
|
break;
|
|
case SHOT_SHELL:
|
|
PlaySound(DIGI_SHOTSHELLSPENT, sp, v3df_none);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (u->ID == GORE_Drip)
|
|
ChangeState(SpriteNum, s_GoreFloorSplash);
|
|
else
|
|
ShrapKillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
// hit something above
|
|
{
|
|
u->zchange = -u->zchange;
|
|
ScaleSpriteVector(SpriteNum, 22000);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// just outright kill it if its boucing around alot
|
|
if (u->bounce > 10)
|
|
{
|
|
KillSprite(SpriteNum);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
ShrapKillSprite(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
short rnd_num;
|
|
|
|
rnd_num = RANDOM_RANGE(1024);
|
|
|
|
ASSERT(u);
|
|
|
|
switch (u->ID)
|
|
{
|
|
case UZI_SHELL:
|
|
if (rnd_num > 854)
|
|
{
|
|
QueueGeneric(SpriteNum,UZI_SHELL + 0);
|
|
}
|
|
else if (rnd_num > 684)
|
|
{
|
|
QueueGeneric(SpriteNum,UZI_SHELL + 1);
|
|
}
|
|
else if (rnd_num > 514)
|
|
{
|
|
QueueGeneric(SpriteNum,UZI_SHELL + 2);
|
|
}
|
|
else if (rnd_num > 344)
|
|
{
|
|
QueueGeneric(SpriteNum,UZI_SHELL + 3);
|
|
}
|
|
else if (rnd_num > 174)
|
|
{
|
|
QueueGeneric(SpriteNum,UZI_SHELL + 4);
|
|
}
|
|
else
|
|
{
|
|
QueueGeneric(SpriteNum,UZI_SHELL + 5);
|
|
}
|
|
|
|
return 0;
|
|
break;
|
|
case SHOT_SHELL:
|
|
if (rnd_num > 683)
|
|
{
|
|
QueueGeneric(SpriteNum,SHOT_SHELL + 1);
|
|
}
|
|
else if (rnd_num > 342)
|
|
{
|
|
QueueGeneric(SpriteNum,SHOT_SHELL + 3);
|
|
}
|
|
else
|
|
{
|
|
QueueGeneric(SpriteNum,SHOT_SHELL + 7);
|
|
}
|
|
return 0;
|
|
break;
|
|
case GORE_Lung:
|
|
if (RANDOM_RANGE(1000) > 500) break;
|
|
sp->clipdist = SPRITEp_SIZE_X(sp);
|
|
SpawnFloorSplash(SpriteNum);
|
|
if (RANDOM_RANGE(1000) < 500)
|
|
PlaySound(DIGI_GIBS1, sp, v3df_none);
|
|
else
|
|
PlaySound(DIGI_GIBS2, sp, v3df_none);
|
|
if (rnd_num > 683)
|
|
{
|
|
QueueGeneric(SpriteNum,900);
|
|
}
|
|
else if (rnd_num > 342)
|
|
{
|
|
QueueGeneric(SpriteNum,901);
|
|
}
|
|
else
|
|
{
|
|
QueueGeneric(SpriteNum,902);
|
|
}
|
|
return 0;
|
|
break;
|
|
case GORE_Liver:
|
|
if (RANDOM_RANGE(1000) > 500) break;
|
|
sp->clipdist = SPRITEp_SIZE_X(sp);
|
|
SpawnFloorSplash(SpriteNum);
|
|
if (RANDOM_RANGE(1000) < 500)
|
|
PlaySound(DIGI_GIBS1, sp, v3df_none);
|
|
else
|
|
PlaySound(DIGI_GIBS2, sp, v3df_none);
|
|
if (rnd_num > 683)
|
|
{
|
|
QueueGeneric(SpriteNum,915);
|
|
}
|
|
else if (rnd_num > 342)
|
|
{
|
|
QueueGeneric(SpriteNum,916);
|
|
}
|
|
else
|
|
{
|
|
QueueGeneric(SpriteNum,917);
|
|
}
|
|
return 0;
|
|
break;
|
|
case GORE_SkullCap:
|
|
if (RANDOM_RANGE(1000) > 500) break;
|
|
sp->clipdist = SPRITEp_SIZE_X(sp);
|
|
SpawnFloorSplash(SpriteNum);
|
|
if (rnd_num > 683)
|
|
{
|
|
QueueGeneric(SpriteNum,930);
|
|
}
|
|
else if (rnd_num > 342)
|
|
{
|
|
QueueGeneric(SpriteNum,931);
|
|
}
|
|
else
|
|
{
|
|
QueueGeneric(SpriteNum,932);
|
|
}
|
|
return 0;
|
|
break;
|
|
case GORE_Head:
|
|
if (RANDOM_RANGE(1000) > 500) break;
|
|
sp->clipdist = SPRITEp_SIZE_X(sp);
|
|
QueueFloorBlood(SpriteNum);
|
|
QueueGeneric(SpriteNum,GORE_Head);
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
// If it wasn't in the switch statement, kill it.
|
|
KillSprite(SpriteNum);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SWBOOL CheckBreakToughness(BREAK_INFOp break_info, short ID)
|
|
{
|
|
////DSPRINTF(ds,"CheckBreakToughness called with %d",ID);
|
|
//CON_Message(ds);
|
|
//MONO_PRINT(ds);
|
|
|
|
if (TEST(break_info->flags, BF_TOUGH))
|
|
{
|
|
switch (ID)
|
|
{
|
|
case LAVA_BOULDER:
|
|
case MIRV_METEOR:
|
|
case SERP_METEOR:
|
|
case BOLT_THINMAN_R0:
|
|
case BOLT_THINMAN_R1:
|
|
case BOLT_THINMAN_R2:
|
|
case BOLT_EXP:
|
|
case TANK_SHELL_EXP:
|
|
case GRENADE_EXP:
|
|
case MICRO_EXP:
|
|
case MINE_EXP:
|
|
case NAP_EXP:
|
|
case SKULL_R0:
|
|
case BETTY_R0:
|
|
case SKULL_SERP:
|
|
case FIREBALL1:
|
|
case GORO_FIREBALL:
|
|
return TRUE; // All the above stuff will break tough things
|
|
}
|
|
return FALSE; // False means it won't break with current weapon
|
|
}
|
|
|
|
return TRUE; // It wasn't tough, go ahead and break it
|
|
}
|
|
|
|
int
|
|
DoItemFly(int16_t SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum];
|
|
USERp u = User[SpriteNum];
|
|
|
|
if (TEST(u->Flags, SPR_UNDERWATER))
|
|
{
|
|
ScaleSpriteVector(SpriteNum, 50000);
|
|
|
|
u->Counter += 20*2;
|
|
u->zchange += u->Counter;
|
|
}
|
|
else
|
|
{
|
|
u->Counter += 60*2;
|
|
u->zchange += u->Counter;
|
|
}
|
|
|
|
u->ret = move_missile(SpriteNum, u->xchange, u->ychange, u->zchange,
|
|
u->ceiling_dist, u->floor_dist, CLIPMASK_MISSILE, MISSILEMOVETICS*2);
|
|
|
|
MissileHitDiveArea(SpriteNum);
|
|
|
|
if (u->ret)
|
|
{
|
|
switch (TEST(u->ret, HIT_MASK))
|
|
{
|
|
case HIT_SPRITE:
|
|
{
|
|
short wall_ang;
|
|
short hit_sprite = -2;
|
|
SPRITEp hsp;
|
|
|
|
hit_sprite = NORM_SPRITE(u->ret);
|
|
hsp = &sprite[hit_sprite];
|
|
|
|
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
{
|
|
wall_ang = NORM_ANGLE(hsp->ang);
|
|
WallBounce(SpriteNum, wall_ang);
|
|
ScaleSpriteVector(SpriteNum, 32000);
|
|
}
|
|
else
|
|
{
|
|
u->xchange = -u->xchange;
|
|
u->ychange = -u->ychange;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HIT_WALL:
|
|
{
|
|
short hit_wall,nw,wall_ang;
|
|
WALLp wph;
|
|
|
|
hit_wall = NORM_WALL(u->ret);
|
|
wph = &wall[hit_wall];
|
|
|
|
nw = wall[hit_wall].point2;
|
|
wall_ang = NORM_ANGLE(getangle(wall[nw].x - wph->x, wall[nw].y - wph->y)+512);
|
|
|
|
WallBounce(SpriteNum, wall_ang);
|
|
ScaleSpriteVector(SpriteNum, 32000);
|
|
break;
|
|
}
|
|
|
|
case HIT_SECTOR:
|
|
{
|
|
// hit floor
|
|
if (sp->z > DIV2(u->hiz + u->loz))
|
|
{
|
|
sp->z = u->loz;
|
|
u->Counter = 0;
|
|
sp->xvel = 0;
|
|
u->zchange = u->xchange = u->ychange = 0;
|
|
return FALSE;
|
|
}
|
|
else
|
|
// hit something above
|
|
{
|
|
u->zchange = -u->zchange;
|
|
ScaleSpriteVector(SpriteNum, 22000);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// This is the FAST queue, it doesn't call any animator functions or states
|
|
int QueueLoWangs(short SpriteNum)
|
|
{
|
|
SPRITEp sp = &sprite[SpriteNum],ps;
|
|
USERp u;
|
|
short NewSprite;
|
|
|
|
|
|
if (TEST(sector[sp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_WATER)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (TEST(sector[sp->sectnum].extra, SECTFX_LIQUID_MASK) == SECTFX_LIQUID_LAVA)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (TestDontStickSector(sp->sectnum))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (LoWangsQueue[LoWangsQueueHead] == -1)
|
|
{
|
|
LoWangsQueue[LoWangsQueueHead] = NewSprite =
|
|
SpawnSprite(STAT_GENERIC_QUEUE, sp->picnum, s_DeadLoWang, sp->sectnum,
|
|
sp->x, sp->y, sp->z, sp->ang, 0);
|
|
}
|
|
else
|
|
{
|
|
// move old sprite to new sprite's place
|
|
setspritez(LoWangsQueue[LoWangsQueueHead], (vec3_t *)sp);
|
|
NewSprite = LoWangsQueue[LoWangsQueueHead];
|
|
ASSERT(sprite[NewSprite].statnum != MAXSTATUS);
|
|
}
|
|
|
|
// Point passed in sprite to ps
|
|
ps = sp;
|
|
sp = &sprite[NewSprite];
|
|
u = User[NewSprite];
|
|
ASSERT(sp);
|
|
sp->owner = -1;
|
|
sp->cstat = 0;
|
|
sp->xrepeat = ps->xrepeat;
|
|
sp->yrepeat = ps->yrepeat;
|
|
sp->shade = ps->shade;
|
|
u->spal = sp->pal = ps->pal;
|
|
change_sprite_stat(NewSprite,STAT_DEFAULT); // Breakable
|
|
SET(sp->cstat, CSTAT_SPRITE_BREAKABLE);
|
|
SET(sp->extra, SPRX_BREAKABLE);
|
|
SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
LoWangsQueueHead = (LoWangsQueueHead+1) & (MAX_LOWANGS_QUEUE-1);
|
|
|
|
return SpriteNum;
|
|
}
|
|
|
|
|
|
#include "saveable.h"
|
|
|
|
static saveable_code saveable_weapon_code[] =
|
|
{
|
|
SAVE_CODE(MissileHitMatch),
|
|
SAVE_CODE(SpawnShrapX),
|
|
SAVE_CODE(DoLavaErupt),
|
|
SAVE_CODE(UserShrapSetup),
|
|
SAVE_CODE(SpawnShrap),
|
|
SAVE_CODE(DoShrapMove),
|
|
SAVE_CODE(DoVomit),
|
|
SAVE_CODE(DoVomit),
|
|
SAVE_CODE(DoVomitSplash),
|
|
SAVE_CODE(DoFastShrapJumpFall),
|
|
SAVE_CODE(DoTracerShrap),
|
|
SAVE_CODE(DoShrapJumpFall),
|
|
SAVE_CODE(DoShrapDamage),
|
|
SAVE_CODE(SpawnBlood),
|
|
SAVE_CODE(VehicleMoveHit),
|
|
SAVE_CODE(WeaponMoveHit),
|
|
SAVE_CODE(DoUziSmoke),
|
|
SAVE_CODE(DoShotgunSmoke),
|
|
SAVE_CODE(DoMineSpark),
|
|
SAVE_CODE(DoFireballFlames),
|
|
SAVE_CODE(DoBreakFlames),
|
|
SAVE_CODE(SetSuicide),
|
|
SAVE_CODE(DoActorScale),
|
|
SAVE_CODE(DoRipperGrow),
|
|
SAVE_CODE(ActorChooseDeath),
|
|
SAVE_CODE(ActorHealth),
|
|
SAVE_CODE(SopDamage),
|
|
SAVE_CODE(SopCheckKill),
|
|
SAVE_CODE(ActorPain),
|
|
SAVE_CODE(ActorPainPlasma),
|
|
SAVE_CODE(ActorStdMissile),
|
|
SAVE_CODE(ActorDamageSlide),
|
|
SAVE_CODE(PlayerDamageSlide),
|
|
SAVE_CODE(GetDamage),
|
|
SAVE_CODE(RadiusGetDamage),
|
|
SAVE_CODE(PlayerCheckDeath),
|
|
SAVE_CODE(PlayerTakeDamage),
|
|
SAVE_CODE(StarBlood),
|
|
SAVE_CODE(DoDamage),
|
|
SAVE_CODE(DoDamageTest),
|
|
SAVE_CODE(DoHitscanDamage),
|
|
SAVE_CODE(DoFlamesDamageTest),
|
|
SAVE_CODE(DoStar),
|
|
SAVE_CODE(DoCrossBolt),
|
|
SAVE_CODE(DoPlasmaDone),
|
|
SAVE_CODE(MissileSeek),
|
|
SAVE_CODE(ComboMissileSeek),
|
|
SAVE_CODE(VectorMissileSeek),
|
|
SAVE_CODE(VectorWormSeek),
|
|
SAVE_CODE(DoBlurExtend),
|
|
SAVE_CODE(InitPlasmaFountain),
|
|
SAVE_CODE(DoPlasmaFountain),
|
|
SAVE_CODE(DoPlasma),
|
|
SAVE_CODE(DoCoolgFire),
|
|
SAVE_CODE(DoEelFire),
|
|
SAVE_CODE(DoGrenade),
|
|
SAVE_CODE(DoVulcanBoulder),
|
|
SAVE_CODE(OwnerIsPlayer),
|
|
SAVE_CODE(DoMineRangeTest),
|
|
SAVE_CODE(DoMineStuck),
|
|
SAVE_CODE(SetMineStuck),
|
|
SAVE_CODE(DoMine),
|
|
SAVE_CODE(DoPuff),
|
|
SAVE_CODE(DoRailPuff),
|
|
SAVE_CODE(DoBoltThinMan),
|
|
SAVE_CODE(DoTracer),
|
|
SAVE_CODE(DoEMP),
|
|
SAVE_CODE(DoEMPBurst),
|
|
SAVE_CODE(DoTankShell),
|
|
SAVE_CODE(DoTracerStart),
|
|
SAVE_CODE(DoLaser),
|
|
SAVE_CODE(DoLaserStart),
|
|
SAVE_CODE(DoRail),
|
|
SAVE_CODE(DoRailStart),
|
|
SAVE_CODE(DoRocket),
|
|
SAVE_CODE(DoMicroMini),
|
|
SAVE_CODE(SpawnExtraMicroMini),
|
|
SAVE_CODE(DoMicro),
|
|
SAVE_CODE(DoUziBullet),
|
|
SAVE_CODE(DoBoltSeeker),
|
|
SAVE_CODE(DoBoltShrapnel),
|
|
SAVE_CODE(DoBoltFatMan),
|
|
SAVE_CODE(DoElectro),
|
|
SAVE_CODE(DoLavaBoulder),
|
|
SAVE_CODE(DoSpear),
|
|
SAVE_CODE(SpawnBasicExp),
|
|
SAVE_CODE(SpawnFireballFlames),
|
|
SAVE_CODE(SpawnBreakFlames),
|
|
SAVE_CODE(SpawnBreakStaticFlames),
|
|
SAVE_CODE(SpawnFireballExp),
|
|
SAVE_CODE(SpawnGoroFireballExp),
|
|
SAVE_CODE(SpawnBoltExp),
|
|
SAVE_CODE(SpawnBunnyExp),
|
|
SAVE_CODE(SpawnTankShellExp),
|
|
SAVE_CODE(SpawnNuclearSecondaryExp),
|
|
SAVE_CODE(SpawnNuclearExp),
|
|
SAVE_CODE(SpawnTracerExp),
|
|
SAVE_CODE(SpawnMicroExp),
|
|
SAVE_CODE(AddSpriteToSectorObject),
|
|
SAVE_CODE(SpawnBigGunFlames),
|
|
SAVE_CODE(SpawnGrenadeSecondaryExp),
|
|
SAVE_CODE(SpawnGrenadeSmallExp),
|
|
SAVE_CODE(SpawnGrenadeExp),
|
|
SAVE_CODE(SpawnMineExp),
|
|
SAVE_CODE(InitMineShrap),
|
|
SAVE_CODE(DoSectorExp),
|
|
SAVE_CODE(SpawnSectorExp),
|
|
SAVE_CODE(SpawnLargeExp),
|
|
SAVE_CODE(SpawnMeteorExp),
|
|
SAVE_CODE(SpawnLittleExp),
|
|
SAVE_CODE(DoFireball),
|
|
SAVE_CODE(DoFindGround),
|
|
SAVE_CODE(DoFindGroundPoint),
|
|
SAVE_CODE(DoNapalm),
|
|
SAVE_CODE(DoBloodWorm),
|
|
SAVE_CODE(DoBloodWorm),
|
|
SAVE_CODE(DoMeteor),
|
|
SAVE_CODE(DoSerpMeteor),
|
|
SAVE_CODE(DoMirvMissile),
|
|
SAVE_CODE(DoMirv),
|
|
SAVE_CODE(MissileSetPos),
|
|
SAVE_CODE(TestMissileSetPos),
|
|
SAVE_CODE(DoRing),
|
|
SAVE_CODE(InitSpellRing),
|
|
SAVE_CODE(DoSerpRing),
|
|
SAVE_CODE(InitLavaFlame),
|
|
SAVE_CODE(InitLavaThrow),
|
|
SAVE_CODE(InitVulcanBoulder),
|
|
SAVE_CODE(InitSerpRing),
|
|
SAVE_CODE(InitSerpRing),
|
|
//SAVE_CODE(InitSerpRing2),
|
|
SAVE_CODE(InitSpellNapalm),
|
|
SAVE_CODE(InitEnemyNapalm),
|
|
SAVE_CODE(InitSpellMirv),
|
|
SAVE_CODE(InitEnemyMirv),
|
|
SAVE_CODE(InitSwordAttack),
|
|
SAVE_CODE(InitFistAttack),
|
|
SAVE_CODE(InitSumoNapalm),
|
|
SAVE_CODE(InitSumoSkull),
|
|
SAVE_CODE(InitSumoStompAttack),
|
|
SAVE_CODE(InitMiniSumoClap),
|
|
SAVE_CODE(WeaponAutoAim),
|
|
SAVE_CODE(WeaponAutoAimZvel),
|
|
SAVE_CODE(AimHitscanToTarget),
|
|
SAVE_CODE(WeaponAutoAimHitscan),
|
|
SAVE_CODE(WeaponHitscanShootFeet),
|
|
SAVE_CODE(InitStar),
|
|
SAVE_CODE(InitHeartAttack),
|
|
SAVE_CODE(InitHeartAttack),
|
|
SAVE_CODE(InitShotgun),
|
|
SAVE_CODE(InitLaser),
|
|
SAVE_CODE(InitRail),
|
|
SAVE_CODE(InitZillaRail),
|
|
SAVE_CODE(InitRocket),
|
|
SAVE_CODE(InitBunnyRocket),
|
|
SAVE_CODE(InitNuke),
|
|
SAVE_CODE(InitEnemyNuke),
|
|
SAVE_CODE(InitMicro),
|
|
SAVE_CODE(InitRipperSlash),
|
|
SAVE_CODE(InitBunnySlash),
|
|
SAVE_CODE(InitSerpSlash),
|
|
SAVE_CODE(WallSpriteInsideSprite),
|
|
SAVE_CODE(DoBladeDamage),
|
|
SAVE_CODE(DoStaticFlamesDamage),
|
|
SAVE_CODE(InitCoolgBash),
|
|
SAVE_CODE(InitSkelSlash),
|
|
SAVE_CODE(InitGoroChop),
|
|
SAVE_CODE(InitHornetSting),
|
|
SAVE_CODE(InitSerpSpell),
|
|
SAVE_CODE(SpawnDemonFist),
|
|
SAVE_CODE(InitSerpMonstSpell),
|
|
SAVE_CODE(DoTeleRipper),
|
|
SAVE_CODE(InitEnemyRocket),
|
|
SAVE_CODE(InitEnemyRail),
|
|
SAVE_CODE(InitZillaRocket),
|
|
SAVE_CODE(InitEnemyStar),
|
|
SAVE_CODE(InitEnemyCrossbow),
|
|
SAVE_CODE(InitSkelSpell),
|
|
SAVE_CODE(InitCoolgFire),
|
|
SAVE_CODE(InitCoolgDrip),
|
|
SAVE_CODE(GenerateDrips),
|
|
SAVE_CODE(InitEelFire),
|
|
SAVE_CODE(InitFireballTrap),
|
|
SAVE_CODE(InitBoltTrap),
|
|
SAVE_CODE(InitEnemyCrossbow),
|
|
SAVE_CODE(InitSpearTrap),
|
|
SAVE_CODE(DoSuicide),
|
|
SAVE_CODE(DoDefaultStat),
|
|
SAVE_CODE(InitTracerUzi),
|
|
SAVE_CODE(InitTracerTurret),
|
|
SAVE_CODE(InitTracerAutoTurret),
|
|
SAVE_CODE(BulletHitSprite),
|
|
SAVE_CODE(HitscanSpriteAdjust),
|
|
SAVE_CODE(InitUzi),
|
|
SAVE_CODE(InitEMP),
|
|
SAVE_CODE(InitTankShell),
|
|
SAVE_CODE(InitTurretMicro),
|
|
SAVE_CODE(InitTurretRocket),
|
|
SAVE_CODE(InitTurretFireball),
|
|
SAVE_CODE(InitTurretRail),
|
|
SAVE_CODE(InitTurretLaser),
|
|
SAVE_CODE(InitSobjMachineGun),
|
|
SAVE_CODE(InitSobjGun),
|
|
SAVE_CODE(SpawnBoatSparks),
|
|
SAVE_CODE(SpawnSwordSparks),
|
|
SAVE_CODE(SpawnTurretSparks),
|
|
SAVE_CODE(SpawnShotgunSparks),
|
|
SAVE_CODE(InitTurretMgun),
|
|
SAVE_CODE(InitEnemyUzi),
|
|
SAVE_CODE(InitGrenade),
|
|
SAVE_CODE(InitSpriteGrenade),
|
|
SAVE_CODE(InitMine),
|
|
SAVE_CODE(InitEnemyMine),
|
|
SAVE_CODE(HelpMissileLateral),
|
|
SAVE_CODE(InitFireball),
|
|
SAVE_CODE(InitEnemyFireball),
|
|
SAVE_CODE(WarpToUnderwater),
|
|
SAVE_CODE(WarpToSurface),
|
|
SAVE_CODE(SpriteWarpToUnderwater),
|
|
SAVE_CODE(SpriteWarpToSurface),
|
|
SAVE_CODE(SpawnSplash),
|
|
SAVE_CODE(SpawnSplashXY),
|
|
SAVE_CODE(SpawnUnderSplash),
|
|
SAVE_CODE(MissileHitDiveArea),
|
|
SAVE_CODE(SpawnBubble),
|
|
SAVE_CODE(DoVehicleSmoke),
|
|
SAVE_CODE(DoWaterSmoke),
|
|
SAVE_CODE(SpawnVehicleSmoke),
|
|
SAVE_CODE(SpawnSmokePuff),
|
|
SAVE_CODE(DoBubble),
|
|
SAVE_CODE(SpriteQueueDelete),
|
|
SAVE_CODE(DoFloorBlood),
|
|
SAVE_CODE(DoWallBlood),
|
|
//SAVE_CODE(DoShellShrap),
|
|
SAVE_CODE(SpawnShell),
|
|
SAVE_CODE(DoShrapVelocity),
|
|
SAVE_CODE(ShrapKillSprite),
|
|
SAVE_CODE(DoItemFly),
|
|
};
|
|
|
|
static saveable_data saveable_weapon_data[] =
|
|
{
|
|
SAVE_DATA(s_NotRestored),
|
|
SAVE_DATA(s_Suicide),
|
|
SAVE_DATA(s_DeadLoWang),
|
|
SAVE_DATA(s_BreakLight),
|
|
SAVE_DATA(s_BreakBarrel),
|
|
SAVE_DATA(s_BreakPedistal),
|
|
SAVE_DATA(s_BreakBottle1),
|
|
SAVE_DATA(s_BreakBottle2),
|
|
SAVE_DATA(s_Puff),
|
|
SAVE_DATA(s_RailPuff),
|
|
SAVE_DATA(sg_RailPuff),
|
|
SAVE_DATA(s_LaserPuff),
|
|
SAVE_DATA(s_Tracer),
|
|
SAVE_DATA(s_EMP),
|
|
SAVE_DATA(s_EMPBurst),
|
|
SAVE_DATA(s_EMPShrap),
|
|
SAVE_DATA(s_TankShell),
|
|
SAVE_DATA(s_VehicleSmoke),
|
|
SAVE_DATA(s_WaterSmoke),
|
|
SAVE_DATA(s_UziSmoke),
|
|
SAVE_DATA(s_ShotgunSmoke),
|
|
SAVE_DATA(s_UziBullet),
|
|
SAVE_DATA(s_UziSpark),
|
|
SAVE_DATA(s_UziPowerSpark),
|
|
SAVE_DATA(s_Bubble),
|
|
SAVE_DATA(s_Splash),
|
|
SAVE_DATA(s_CrossBolt),
|
|
SAVE_DATA(sg_CrossBolt),
|
|
SAVE_DATA(s_Star),
|
|
SAVE_DATA(s_StarStuck),
|
|
SAVE_DATA(s_StarDown),
|
|
SAVE_DATA(s_StarDownStuck),
|
|
SAVE_DATA(s_LavaBoulder),
|
|
SAVE_DATA(s_LavaShard),
|
|
SAVE_DATA(s_VulcanBoulder),
|
|
SAVE_DATA(s_Grenade),
|
|
SAVE_DATA(s_Grenade),
|
|
SAVE_DATA(sg_Grenade),
|
|
SAVE_DATA(s_MineStuck),
|
|
SAVE_DATA(s_Mine),
|
|
SAVE_DATA(s_MineSpark),
|
|
SAVE_DATA(s_Meteor),
|
|
SAVE_DATA(sg_Meteor),
|
|
SAVE_DATA(s_MeteorExp),
|
|
SAVE_DATA(s_MirvMeteor),
|
|
SAVE_DATA(sg_MirvMeteor),
|
|
SAVE_DATA(s_MirvMeteorExp),
|
|
SAVE_DATA(s_SerpMeteor),
|
|
SAVE_DATA(sg_SerpMeteor),
|
|
SAVE_DATA(s_SerpMeteorExp),
|
|
SAVE_DATA(s_Spear),
|
|
SAVE_DATA(sg_Spear),
|
|
SAVE_DATA(s_Rocket),
|
|
SAVE_DATA(sg_Rocket),
|
|
SAVE_DATA(s_BunnyRocket),
|
|
SAVE_DATA(sg_BunnyRocket),
|
|
SAVE_DATA(s_Rail),
|
|
SAVE_DATA(sg_Rail),
|
|
SAVE_DATA(s_Laser),
|
|
//SAVE_DATA(s_MicroPuff),
|
|
SAVE_DATA(s_Micro),
|
|
SAVE_DATA(sg_Micro),
|
|
SAVE_DATA(s_MicroMini),
|
|
SAVE_DATA(sg_MicroMini),
|
|
SAVE_DATA(s_BoltThinMan),
|
|
SAVE_DATA(sg_BoltThinMan),
|
|
SAVE_DATA(s_BoltSeeker),
|
|
SAVE_DATA(sg_BoltSeeker),
|
|
SAVE_DATA(s_BoltFatMan),
|
|
SAVE_DATA(s_BoltShrapnel),
|
|
SAVE_DATA(s_CoolgFire),
|
|
SAVE_DATA(s_CoolgFireDone),
|
|
SAVE_DATA(s_CoolgDrip),
|
|
SAVE_DATA(s_GoreFloorSplash),
|
|
SAVE_DATA(s_GoreSplash),
|
|
SAVE_DATA(s_Plasma),
|
|
SAVE_DATA(s_PlasmaFountain),
|
|
SAVE_DATA(s_PlasmaDrip),
|
|
SAVE_DATA(s_PlasmaDone),
|
|
SAVE_DATA(s_TeleportEffect),
|
|
SAVE_DATA(s_TeleportEffect2),
|
|
SAVE_DATA(s_Electro),
|
|
SAVE_DATA(s_ElectroShrap),
|
|
SAVE_DATA(s_GrenadeExp),
|
|
SAVE_DATA(s_GrenadeSmallExp),
|
|
SAVE_DATA(s_GrenadeExp),
|
|
SAVE_DATA(s_MineExp),
|
|
SAVE_DATA(s_BasicExp),
|
|
SAVE_DATA(s_MicroExp),
|
|
SAVE_DATA(s_BigGunFlame),
|
|
SAVE_DATA(s_BoltExp),
|
|
SAVE_DATA(s_TankShellExp),
|
|
SAVE_DATA(s_TracerExp),
|
|
SAVE_DATA(s_SectorExp),
|
|
SAVE_DATA(s_FireballExp),
|
|
SAVE_DATA(s_NapExp),
|
|
SAVE_DATA(s_FireballFlames),
|
|
SAVE_DATA(s_BreakFlames),
|
|
SAVE_DATA(s_Fireball),
|
|
SAVE_DATA(s_Fireball),
|
|
//SAVE_DATA(sg_Fireball),
|
|
SAVE_DATA(s_Ring),
|
|
//SAVE_DATA(sg_Ring),
|
|
SAVE_DATA(s_Ring),
|
|
SAVE_DATA(s_Ring2),
|
|
SAVE_DATA(s_Napalm),
|
|
SAVE_DATA(s_BloodWorm),
|
|
SAVE_DATA(s_BloodWorm),
|
|
SAVE_DATA(s_PlasmaExp),
|
|
SAVE_DATA(s_PlasmaExp),
|
|
SAVE_DATA(s_Mirv),
|
|
SAVE_DATA(s_MirvMissile),
|
|
SAVE_DATA(s_Vomit1),
|
|
SAVE_DATA(s_Vomit2),
|
|
SAVE_DATA(s_VomitSplash),
|
|
SAVE_DATA(s_GoreHead),
|
|
SAVE_DATA(s_GoreLeg),
|
|
SAVE_DATA(s_GoreEye),
|
|
SAVE_DATA(s_GoreTorso),
|
|
SAVE_DATA(s_GoreArm),
|
|
SAVE_DATA(s_GoreLung),
|
|
SAVE_DATA(s_GoreLiver),
|
|
SAVE_DATA(s_GoreSkullCap),
|
|
SAVE_DATA(s_GoreChunkS),
|
|
SAVE_DATA(s_GoreDrip),
|
|
SAVE_DATA(s_FastGoreDrip),
|
|
SAVE_DATA(s_GoreFlame),
|
|
SAVE_DATA(s_TracerShrap),
|
|
SAVE_DATA(s_UziShellShrap),
|
|
SAVE_DATA(s_UziShellShrapStill1),
|
|
SAVE_DATA(s_UziShellShrapStill2),
|
|
SAVE_DATA(s_UziShellShrapStill3),
|
|
SAVE_DATA(s_UziShellShrapStill4),
|
|
SAVE_DATA(s_UziShellShrapStill5),
|
|
SAVE_DATA(s_UziShellShrapStill6),
|
|
SAVE_DATA(s_ShotgunShellShrap),
|
|
SAVE_DATA(s_ShotgunShellShrapStill1),
|
|
SAVE_DATA(s_ShotgunShellShrapStill2),
|
|
SAVE_DATA(s_ShotgunShellShrapStill3),
|
|
SAVE_DATA(s_GoreFlameChunkA),
|
|
SAVE_DATA(s_GoreFlameChunkB),
|
|
SAVE_DATA(s_CoinShrap),
|
|
SAVE_DATA(s_Marbel),
|
|
SAVE_DATA(s_GlassShrapA),
|
|
SAVE_DATA(s_GlassShrapB),
|
|
SAVE_DATA(s_GlassShrapC),
|
|
SAVE_DATA(s_WoodShrapA),
|
|
SAVE_DATA(s_WoodShrapB),
|
|
SAVE_DATA(s_WoodShrapC),
|
|
SAVE_DATA(s_StoneShrapA),
|
|
SAVE_DATA(s_StoneShrapB),
|
|
SAVE_DATA(s_StoneShrapC),
|
|
SAVE_DATA(s_MetalShrapA),
|
|
SAVE_DATA(s_MetalShrapB),
|
|
SAVE_DATA(s_MetalShrapC),
|
|
SAVE_DATA(s_PaperShrapA),
|
|
SAVE_DATA(s_PaperShrapB),
|
|
SAVE_DATA(s_PaperShrapC),
|
|
SAVE_DATA(s_FloorBlood1),
|
|
SAVE_DATA(s_FootPrint1),
|
|
SAVE_DATA(s_FootPrint2),
|
|
SAVE_DATA(s_FootPrint3),
|
|
SAVE_DATA(s_WallBlood1),
|
|
SAVE_DATA(s_WallBlood2),
|
|
SAVE_DATA(s_WallBlood3),
|
|
SAVE_DATA(s_WallBlood4),
|
|
};
|
|
|
|
saveable_module saveable_weapon =
|
|
{
|
|
// code
|
|
saveable_weapon_code,
|
|
SIZ(saveable_weapon_code),
|
|
|
|
// data
|
|
saveable_weapon_data,
|
|
SIZ(saveable_weapon_data)
|
|
};
|
|
END_SW_NS
|