08 Aug 2009
This commit is contained in:
parent
8eb90a3a54
commit
a6875e7cf1
|
@ -564,7 +564,6 @@ void V_RenderPlaque( void )
|
|||
const char *levelshot;
|
||||
|
||||
levelshot = CVAR_GET_STRING( "cl_levelshot_name" );
|
||||
if( !strcmp( levelshot, "" )) levelshot = "*black";
|
||||
|
||||
// logo that shows up while upload next level
|
||||
DrawImageRectangle( SPR_Load( levelshot ));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "common.h"
|
||||
#include "client.h"
|
||||
|
||||
#define SCRSHOT_TYPE "tga"
|
||||
#define SCRSHOT_TYPE "jpg"
|
||||
|
||||
/*
|
||||
================
|
||||
|
@ -193,7 +193,7 @@ void CL_LevelShot_f( void )
|
|||
|
||||
if( !cl.need_levelshot ) return;
|
||||
// check for exist
|
||||
com.sprintf( checkname, "levelshots/%s.png", cl.configstrings[CS_NAME] );
|
||||
com.sprintf( checkname, "levelshots/%s.jpg", cl.configstrings[CS_NAME] );
|
||||
if( !FS_FileExists( checkname )) re->ScrShot( checkname, true );
|
||||
cl.need_levelshot = false; // done
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ Set a specific sky and rotation speed
|
|||
*/
|
||||
void CL_SetSky_f( void )
|
||||
{
|
||||
if(Cmd_Argc() < 2)
|
||||
if( Cmd_Argc() < 2 )
|
||||
{
|
||||
Msg( "Usage: sky <shadername>\n" );
|
||||
return;
|
||||
|
|
|
@ -261,8 +261,8 @@ void CL_ParseServerData( sizebuf_t *msg )
|
|||
break;
|
||||
if( i == 3 )
|
||||
{
|
||||
Cvar_Set( "cl_levelshot_name", "" );
|
||||
cl.need_levelshot = true; // make levelshot
|
||||
Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen
|
||||
cl.need_levelshot = true; // make levelshot
|
||||
}
|
||||
// seperate the printfs so the server message can have a color
|
||||
Msg("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n");
|
||||
|
|
|
@ -218,55 +218,16 @@ typedef struct tga_s
|
|||
|
||||
========================================================================
|
||||
*/
|
||||
typedef struct huffman_table_s
|
||||
{
|
||||
// Huffman coding tables
|
||||
byte bits[16];
|
||||
byte hval[256];
|
||||
byte size[256];
|
||||
word code[256];
|
||||
} huffman_table_t;
|
||||
// defined in image_jpg.c
|
||||
|
||||
typedef struct jpg_s
|
||||
{
|
||||
// not a real header
|
||||
file_t *file; // file
|
||||
byte *buffer; // jpg buffer
|
||||
|
||||
int width; // width image
|
||||
int height; // height image
|
||||
byte *data; // image
|
||||
int data_precision; // bit per component
|
||||
int num_components; // number component
|
||||
int restart_interval; // restart interval
|
||||
bool progressive_mode; // progressive format
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
struct
|
||||
{
|
||||
int id; // identifier
|
||||
int h; // horizontal sampling factor
|
||||
int v; // vertical sampling factor
|
||||
int t; // quantization table selector
|
||||
int td; // DC table selector
|
||||
int ta; // AC table selector
|
||||
} component_info[3]; // RGB (alpha not supported)
|
||||
|
||||
huffman_table_t hac[4]; // AC table
|
||||
huffman_table_t hdc[4]; // DC table
|
||||
.PNG image format
|
||||
|
||||
int qtable[4][64]; // quantization table
|
||||
|
||||
struct
|
||||
{
|
||||
int ss,se; // progressive jpeg spectral selection
|
||||
int ah,al; // progressive jpeg successive approx
|
||||
} scan;
|
||||
|
||||
int dc[3];
|
||||
int curbit;
|
||||
byte curbyte;
|
||||
|
||||
} jpg_t;
|
||||
========================================================================
|
||||
*/
|
||||
// defined in image_png.c
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
@ -494,6 +455,7 @@ extern imglib_t image;
|
|||
extern cvar_t *img_oldformats;
|
||||
extern cvar_t *fs_wadsupport;
|
||||
extern cvar_t *png_compression;
|
||||
extern cvar_t *jpg_quality;
|
||||
extern byte *fs_mempool;
|
||||
extern const bpc_desc_t PFDesc[];
|
||||
|
||||
|
@ -546,6 +508,7 @@ bool Image_SaveTGA( const char *name, rgbdata_t *pix );
|
|||
bool Image_SaveDDS( const char *name, rgbdata_t *pix );
|
||||
bool Image_SaveBMP( const char *name, rgbdata_t *pix );
|
||||
bool Image_SavePNG( const char *name, rgbdata_t *pix );
|
||||
bool Image_SaveJPG( const char *name, rgbdata_t *pix );
|
||||
bool Image_SavePCX( const char *name, rgbdata_t *pix );
|
||||
|
||||
//
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,498 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2007 ©
|
||||
// img_jpg.c - jpg format load & save
|
||||
//=======================================================================
|
||||
|
||||
#include "imagelib.h"
|
||||
|
||||
jpg_t jpg_file; // jpeg read struct
|
||||
|
||||
int jpeg_read_byte( void )
|
||||
{
|
||||
// read byte
|
||||
jpg_file.curbyte = *jpg_file.buffer++;
|
||||
jpg_file.curbit = 0;
|
||||
return jpg_file.curbyte;
|
||||
}
|
||||
|
||||
int jpeg_read_word( void )
|
||||
{
|
||||
// read word
|
||||
word i = BuffLittleShort( jpg_file.buffer);
|
||||
i = ((i << 8) & 0xFF00) + ((i >> 8) & 0x00FF);
|
||||
jpg_file.buffer += 2;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int jpeg_read_bit( void )
|
||||
{
|
||||
// read bit
|
||||
register int i;
|
||||
if(jpg_file.curbit == 0)
|
||||
{
|
||||
jpeg_read_byte();
|
||||
if(jpg_file.curbyte == 0xFF)
|
||||
{
|
||||
while(jpg_file.curbyte == 0xFF) jpeg_read_byte();
|
||||
if(jpg_file.curbyte >= 0xD0 && jpg_file.curbyte <= 0xD7)
|
||||
Mem_Set(jpg_file.dc, 0, sizeof(int) * 3);
|
||||
if(jpg_file.curbyte == 0) jpg_file.curbyte = 0xFF;
|
||||
else jpeg_read_byte();
|
||||
}
|
||||
}
|
||||
|
||||
i = (jpg_file.curbyte >> (7 - jpg_file.curbit++)) & 0x01;
|
||||
if(jpg_file.curbit == 8) jpg_file.curbit = 0;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int jpeg_read_bits( int num )
|
||||
{
|
||||
// read num bit
|
||||
register int i, j;
|
||||
|
||||
for(i = 0, j = 0; i < num; i++)
|
||||
{
|
||||
j <<= 1;
|
||||
j |= jpeg_read_bit();
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
int jpeg_bit2int( int bit, int i )
|
||||
{
|
||||
// convert bit code to int
|
||||
if((i & (1 << (bit - 1))) > 0) return i;
|
||||
return -(i ^ ((1 << bit) - 1));
|
||||
}
|
||||
|
||||
int jpeg_huffmancode( huffman_table_t *table )
|
||||
{
|
||||
// get Huffman code
|
||||
register int i,size,code;
|
||||
for(size = 1, code = 0, i = 0; size < 17; size++)
|
||||
{
|
||||
code <<= 1;
|
||||
code |= jpeg_read_bit();
|
||||
while(table->size[i] <= size)
|
||||
{
|
||||
if( table->code[i] == code )
|
||||
return table->hval[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
void jpeg_idct( float *data )
|
||||
{
|
||||
// aa&n algorithm inverse DCT
|
||||
float t0, t1, t2, t3, t4, t5, t6, t7;
|
||||
float t10, t11, t12, t13;
|
||||
float z5, z10, z11, z12, z13;
|
||||
float *dataptr;
|
||||
int i;
|
||||
|
||||
dataptr = data;
|
||||
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
t0 = dataptr[8 * 0];
|
||||
t1 = dataptr[8 * 2];
|
||||
t2 = dataptr[8 * 4];
|
||||
t3 = dataptr[8 * 6];
|
||||
|
||||
t10 = t0 + t2;
|
||||
t11 = t0 - t2;
|
||||
t13 = t1 + t3;
|
||||
t12 = - t13 + (t1 - t3) * 1.414213562;//??
|
||||
|
||||
t0 = t10 + t13;
|
||||
t3 = t10 - t13;
|
||||
t1 = t11 + t12;
|
||||
t2 = t11 - t12;
|
||||
t4 = dataptr[8 * 1];
|
||||
t5 = dataptr[8 * 3];
|
||||
t6 = dataptr[8 * 5];
|
||||
t7 = dataptr[8 * 7];
|
||||
|
||||
z13 = t6 + t5;
|
||||
z10 = t6 - t5;
|
||||
z11 = t4 + t7;
|
||||
z12 = t4 - t7;
|
||||
|
||||
t7 = z11 + z13;
|
||||
t11 = (z11 - z13) * 1.414213562;
|
||||
z5 = (z10 + z12) * 1.847759065;
|
||||
t10 = - z5 + z12 * 1.082392200;
|
||||
t12 = z5 - z10 * 2.613125930;
|
||||
t6 = t12 - t7;
|
||||
t5 = t11 - t6;
|
||||
t4 = t10 + t5;
|
||||
|
||||
dataptr[8 * 0] = t0 + t7;
|
||||
dataptr[8 * 7] = t0 - t7;
|
||||
dataptr[8 * 1] = t1 + t6;
|
||||
dataptr[8 * 6] = t1 - t6;
|
||||
dataptr[8 * 2] = t2 + t5;
|
||||
dataptr[8 * 5] = t2 - t5;
|
||||
dataptr[8 * 4] = t3 + t4;
|
||||
dataptr[8 * 3] = t3 - t4;
|
||||
dataptr++;
|
||||
}
|
||||
|
||||
dataptr = data;
|
||||
|
||||
for(i = 0; i < 8; i++)
|
||||
{
|
||||
t10 = dataptr[0] + dataptr[4];
|
||||
t11 = dataptr[0] - dataptr[4];
|
||||
t13 = dataptr[2] + dataptr[6];
|
||||
t12 = - t13 + (dataptr[2] - dataptr[6]) * 1.414213562;//??
|
||||
|
||||
t0 = t10 + t13;
|
||||
t3 = t10 - t13;
|
||||
t1 = t11 + t12;
|
||||
t2 = t11 - t12;
|
||||
|
||||
z13 = dataptr[5] + dataptr[3];
|
||||
z10 = dataptr[5] - dataptr[3];
|
||||
z11 = dataptr[1] + dataptr[7];
|
||||
z12 = dataptr[1] - dataptr[7];
|
||||
|
||||
t7 = z11 + z13;
|
||||
t11 = (z11 - z13) * 1.414213562;
|
||||
z5 = (z10 + z12) * 1.847759065;
|
||||
t10 = - z5 + z12 * 1.082392200;
|
||||
t12 = z5 - z10 * 2.613125930;
|
||||
|
||||
t6 = t12 - t7;
|
||||
t5 = t11 - t6;
|
||||
t4 = t10 + t5;
|
||||
|
||||
dataptr[0] = t0 + t7;
|
||||
dataptr[7] = t0 - t7;
|
||||
dataptr[1] = t1 + t6;
|
||||
dataptr[6] = t1 - t6;
|
||||
dataptr[2] = t2 + t5;
|
||||
dataptr[5] = t2 - t5;
|
||||
dataptr[4] = t3 + t4;
|
||||
dataptr[3] = t3 - t4;
|
||||
dataptr += 8;//move ptr
|
||||
}
|
||||
}
|
||||
|
||||
int jpeg_readmarkers( void )
|
||||
{
|
||||
// read jpeg markers
|
||||
int marker, length, i, j, k, l, m;
|
||||
huffman_table_t *hptr;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
marker = jpeg_read_byte();
|
||||
if( marker != 0xFF ) return 0;
|
||||
|
||||
marker = jpeg_read_byte();
|
||||
if( marker != 0xD8 )
|
||||
{
|
||||
length = jpeg_read_word();
|
||||
length -= 2;
|
||||
|
||||
switch( marker )
|
||||
{
|
||||
case 0xC0: // baseline
|
||||
jpg_file.data_precision = jpeg_read_byte();
|
||||
jpg_file.height = jpeg_read_word();
|
||||
jpg_file.width = jpeg_read_word();
|
||||
jpg_file.num_components = jpeg_read_byte();
|
||||
if(length - 6 != jpg_file.num_components * 3) return 0;
|
||||
|
||||
for(i = 0; i < jpg_file.num_components; i++)
|
||||
{
|
||||
jpg_file.component_info[i].id = jpeg_read_byte();
|
||||
j = jpeg_read_byte();
|
||||
jpg_file.component_info[i].h = (j >> 4) & 0x0F;
|
||||
jpg_file.component_info[i].v = j & 0x0F;
|
||||
jpg_file.component_info[i].t = jpeg_read_byte();
|
||||
}
|
||||
break;
|
||||
case 0xC1: // extended sequetial, Huffman
|
||||
case 0xC2: // progressive, Huffman
|
||||
case 0xC3: // lossless, Huffman
|
||||
case 0xC5: // differential sequential, Huffman
|
||||
case 0xC6: // differential progressive, Huffman
|
||||
case 0xC7: // differential lossless, Huffman
|
||||
case 0xC8: // reserved for JPEG extensions
|
||||
case 0xC9: // extended sequential, arithmetic
|
||||
case 0xCA: // progressive, arithmetic
|
||||
case 0xCB: // lossless, arithmetic
|
||||
case 0xCD: // differential sequential, arithmetic
|
||||
case 0xCE: // differential progressive, arithmetic
|
||||
case 0xCF: // differential lossless, arithmetic
|
||||
return 0; // not supported yet
|
||||
case 0xC4: // huffman table
|
||||
while( length > 0 )
|
||||
{
|
||||
k = jpeg_read_byte();
|
||||
if(k & 0x10) hptr = &jpg_file.hac[k & 0x0F];
|
||||
else hptr = &jpg_file.hdc[k & 0x0F];
|
||||
for(i = 0, j = 0; i < 16; i++)
|
||||
{
|
||||
hptr->bits[i] = jpeg_read_byte();
|
||||
j += hptr->bits[i];
|
||||
}
|
||||
length -= 17;
|
||||
for(i = 0; i < j; i++) hptr->hval[i] = jpeg_read_byte();
|
||||
length -= j;
|
||||
|
||||
for(i = 0, k = 0, l = 0; i < 16; i++)
|
||||
{
|
||||
for(j = 0; j < hptr->bits[i]; j++, k++)
|
||||
{
|
||||
hptr->size[k] = i + 1;
|
||||
hptr->code[k] = l++;
|
||||
}
|
||||
l <<= 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xDB: // quantization table
|
||||
while( length > 0 )
|
||||
{
|
||||
j = jpeg_read_byte();
|
||||
k = (j >> 4) & 0x0F;
|
||||
for(i = 0; i < 64; i++)
|
||||
{
|
||||
if( k )jpg_file.qtable[j][i] = jpeg_read_word();
|
||||
else jpg_file.qtable[j][i] = jpeg_read_byte();
|
||||
}
|
||||
length -= 65;
|
||||
if( k )length -= 64;
|
||||
}
|
||||
break;
|
||||
case 0xD9: // end of image (EOI)
|
||||
return 0;
|
||||
case 0xDA: // start of scan (SOS)
|
||||
j = jpeg_read_byte();
|
||||
for(i = 0; i < j; i++)
|
||||
{
|
||||
k = jpeg_read_byte();
|
||||
m = jpeg_read_byte();
|
||||
for( l = 0; l < jpg_file.num_components; l++ )
|
||||
{
|
||||
if( jpg_file.component_info[l].id == k )
|
||||
{
|
||||
jpg_file.component_info[l].td = (m >> 4) & 0x0F;
|
||||
jpg_file.component_info[l].ta = m & 0x0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
jpg_file.scan.ss = jpeg_read_byte();
|
||||
jpg_file.scan.se = jpeg_read_byte();
|
||||
k = jpeg_read_byte();
|
||||
jpg_file.scan.ah = (k >> 4) & 0x0F;
|
||||
jpg_file.scan.al = k & 0x0F;
|
||||
return 1;
|
||||
case 0xDD: // restart interval
|
||||
jpg_file.restart_interval = jpeg_read_word();
|
||||
break;
|
||||
default:
|
||||
jpg_file.buffer += length; // move ptr
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void jpeg_decompress( void )
|
||||
{
|
||||
// decompress jpeg file (baseline algorithm)
|
||||
register int x, y, i, j, k, l, c;
|
||||
int X, Y, H, V, plane, scaleh[3], scalev[3];
|
||||
static float vector[64], dct[64];
|
||||
|
||||
static const int jpeg_zigzag[64] =
|
||||
{
|
||||
0, 1, 5, 6, 14, 15, 27, 28,
|
||||
2, 4, 7, 13, 16, 26, 29, 42,
|
||||
3, 8, 12, 17, 25, 30, 41, 43,
|
||||
9, 11, 18, 24, 31, 40, 44, 53,
|
||||
10, 19, 23, 32, 39, 45, 52, 54,
|
||||
20, 22, 33, 38, 46, 51, 55, 60,
|
||||
21, 34, 37, 47, 50, 56, 59, 61,
|
||||
35, 36, 48, 49, 57, 58, 62, 63
|
||||
};
|
||||
|
||||
// 1.0, k = 0; cos(k * PI / 16) * sqrt(2), k = 1...7
|
||||
static const float aanscale[8] =
|
||||
{
|
||||
1.0, 1.387039845, 1.306562965, 1.175875602,
|
||||
1.0, 0.785694958, 0.541196100, 0.275899379
|
||||
};
|
||||
|
||||
scaleh[0] = 1;
|
||||
scalev[0] = 1;
|
||||
|
||||
if(jpg_file.num_components == 3)
|
||||
{
|
||||
scaleh[1] = jpg_file.component_info[0].h / jpg_file.component_info[1].h;
|
||||
scalev[1] = jpg_file.component_info[0].v / jpg_file.component_info[1].v;
|
||||
scaleh[2] = jpg_file.component_info[0].h / jpg_file.component_info[2].h;
|
||||
scalev[2] = jpg_file.component_info[0].v / jpg_file.component_info[2].v;
|
||||
}
|
||||
Mem_Set( jpg_file.dc, 0, sizeof(int) * 3);
|
||||
|
||||
for( Y = 0; Y < jpg_file.height; Y += jpg_file.component_info[0].v << 3 )
|
||||
{
|
||||
if( jpg_file.restart_interval > 0 ) jpg_file.curbit = 0;
|
||||
for( X = 0; X < jpg_file.width; X += jpg_file.component_info[0].h << 3 )
|
||||
{
|
||||
for(plane = 0; plane < jpg_file.num_components; plane++)
|
||||
{
|
||||
for(V = 0; V < jpg_file.component_info[plane].v; V++)
|
||||
{
|
||||
for(H = 0; H < jpg_file.component_info[plane].h; H++)
|
||||
{
|
||||
i = jpeg_huffmancode(&jpg_file.hdc[jpg_file.component_info[plane].td]);
|
||||
i &= 0x0F;
|
||||
vector[0] = jpg_file.dc[plane] + jpeg_bit2int(i,jpeg_read_bits(i));
|
||||
jpg_file.dc[plane] = vector[0];
|
||||
i = 1;
|
||||
|
||||
while(i < 64)
|
||||
{
|
||||
j = jpeg_huffmancode(&jpg_file.hac[jpg_file.component_info[plane].ta]);
|
||||
if(j == 0) while(i < 64) vector[i++] = 0;
|
||||
else
|
||||
{
|
||||
k = i + ((j >> 4) & 0x0F);
|
||||
while(i < k) vector[i++] = 0;
|
||||
j &= 0x0F;
|
||||
vector[i++] = jpeg_bit2int(j,jpeg_read_bits(j));
|
||||
}
|
||||
}
|
||||
|
||||
k = jpg_file.component_info[plane].t;
|
||||
for(y = 0, i = 0; y < 8; y++)
|
||||
{
|
||||
for(x = 0; x < 8; x++, i++)
|
||||
{
|
||||
j = jpeg_zigzag[i];
|
||||
dct[i] = vector[j] * jpg_file.qtable[k][j] * aanscale[x] * aanscale[y];
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_idct(dct);
|
||||
for(y = 0; y < 8; y++)
|
||||
{
|
||||
for(x = 0; x < 8; x++)
|
||||
{
|
||||
c = ((int)dct[(y << 3) + x] >> 3) + 128;
|
||||
if(c < 0) c = 0;
|
||||
else if(c > 255) c = 255;
|
||||
|
||||
if(scaleh[plane] == 1 && scalev[plane] == 1)
|
||||
{
|
||||
i = X + x + (H << 3);
|
||||
j = Y + y + (V << 3);
|
||||
if(i < jpg_file.width && j < jpg_file.height)
|
||||
jpg_file.data[((j * jpg_file.width + i) << 2) + plane] = c;
|
||||
}
|
||||
else for(l = 0; l < scalev[plane]; l++)//else for, heh...
|
||||
{
|
||||
for(k = 0; k < scaleh[plane]; k++)
|
||||
{
|
||||
i = X + (x + (H << 3)) * scaleh[plane] + k;
|
||||
j = Y + (y + (V << 3)) * scalev[plane] + l;
|
||||
if(i < jpg_file.width && j < jpg_file.height)
|
||||
jpg_file.data[((j * jpg_file.width + i) << 2) + plane] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jpeg_ycbcr2rgba( void )
|
||||
{
|
||||
int i, Y, Cb, Cr, R, G, B;
|
||||
|
||||
// convert YCbCr image to RGBA
|
||||
for( i = 0; i < jpg_file.width * jpg_file.height << 2; i += 4 )
|
||||
{
|
||||
Y = jpg_file.data[i+0];
|
||||
Cb = jpg_file.data[i+1] - 128;
|
||||
Cr = jpg_file.data[i+2] - 128;
|
||||
|
||||
R = Y + 1.40200 * Cr;
|
||||
G = Y - 0.34414 * Cb - 0.71414 * Cr;
|
||||
B = Y + 1.77200 * Cb;
|
||||
|
||||
// bound colors
|
||||
R = bound( 0, R, 255 );
|
||||
G = bound( 0, G, 255 );
|
||||
B = bound( 0, B, 255 );
|
||||
|
||||
jpg_file.data[i+0] = R;
|
||||
jpg_file.data[i+1] = G;
|
||||
jpg_file.data[i+2] = B;
|
||||
jpg_file.data[i+3] = 0xff; // no alpha channel
|
||||
}
|
||||
image.flags |= IMAGE_HAS_COLOR;
|
||||
}
|
||||
|
||||
void jpeg_gray2rgba( void )
|
||||
{
|
||||
int i, j;
|
||||
|
||||
// grayscale image to RGBA
|
||||
for(i = 0; i < jpg_file.width * jpg_file.height << 2; i += 4)
|
||||
{
|
||||
j = jpg_file.data[i];
|
||||
jpg_file.data[i+0] = j;
|
||||
jpg_file.data[i+1] = j;
|
||||
jpg_file.data[i+2] = j;
|
||||
jpg_file.data[i+3] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
Image_LoadJPG
|
||||
=============
|
||||
*/
|
||||
bool Image_LoadJPG( const char *name, const byte *buffer, size_t filesize )
|
||||
{
|
||||
Mem_Set( &jpg_file, 0, sizeof( jpg_file ));
|
||||
jpg_file.buffer = (byte *)buffer;
|
||||
|
||||
if(!jpeg_readmarkers())
|
||||
return false; // it's not a jpg file, just skip it
|
||||
|
||||
image.width = jpg_file.width;
|
||||
image.height = jpg_file.height;
|
||||
if(!Image_ValidSize( name )) return false;
|
||||
|
||||
image.size = jpg_file.width * jpg_file.height * 4;
|
||||
jpg_file.data = Mem_Alloc( Sys.imagepool, image.size );
|
||||
|
||||
jpeg_decompress();
|
||||
if( jpg_file.num_components == 1 ) jpeg_gray2rgba();
|
||||
if( jpg_file.num_components == 3 ) jpeg_ycbcr2rgba();
|
||||
|
||||
image.rgba = jpg_file.data;
|
||||
image.type = PF_RGBA_32;
|
||||
image.depth = 1;
|
||||
image.num_mips = 1;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -16,7 +16,17 @@ typedef struct suffix_s
|
|||
image_hint_t hint;
|
||||
} suffix_t;
|
||||
|
||||
static const suffix_t skybox_3ds[6] =
|
||||
static const suffix_t skybox_qv1[6] =
|
||||
{
|
||||
{ "ft", IMAGE_FLIP_X, CB_HINT_POSX },
|
||||
{ "bk", IMAGE_FLIP_Y, CB_HINT_NEGX },
|
||||
{ "up", IMAGE_ROT_90, CB_HINT_POSZ },
|
||||
{ "dn", IMAGE_ROT_90, CB_HINT_NEGZ },
|
||||
{ "rt", IMAGE_ROT_90, CB_HINT_POSY },
|
||||
{ "lf", IMAGE_ROT270, CB_HINT_NEGY },
|
||||
};
|
||||
|
||||
static const suffix_t skybox_qv2[6] =
|
||||
{
|
||||
{ "_ft", IMAGE_FLIP_X, CB_HINT_POSX },
|
||||
{ "_bk", IMAGE_FLIP_Y, CB_HINT_NEGX },
|
||||
|
@ -54,7 +64,8 @@ typedef struct cubepack_s
|
|||
|
||||
static const cubepack_t load_cubemap[] =
|
||||
{
|
||||
{ "3Ds Sky ", skybox_3ds },
|
||||
{ "3Ds Sky1", skybox_qv1 },
|
||||
{ "3Ds Sky2", skybox_qv2 },
|
||||
{ "3Ds Cube", cubemap_v2 },
|
||||
{ "Tenebrae", cubemap_v1 }, // FIXME: remove this ?
|
||||
{ NULL, NULL },
|
||||
|
@ -312,7 +323,6 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
|
|||
if( !com.strnicmp( suffix, cmap->type[i].suf, suflen ))
|
||||
{
|
||||
com.strncpy( path, loadname, com.strlen( loadname ) - suflen + 1 );
|
||||
Msg( "path %s - %s\n", path, suffix );
|
||||
FS_DefaultExtension( path, ".dds" );
|
||||
image.filter = cmap->type[i].hint; // install side hint
|
||||
f = FS_LoadFile( path, &filesize );
|
||||
|
|
|
@ -241,6 +241,7 @@ bool Image_SaveTGA( const char *name, rgbdata_t *pix )
|
|||
case PF_BGRA_32: pixel_size = 4; break;
|
||||
default:
|
||||
MsgDev( D_ERROR, "Image_SaveTGA: unsupported image type %s\n", PFDesc[pix->type].name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ cvar_t *image_profile;
|
|||
cvar_t *gl_round_down;
|
||||
cvar_t *fs_textures;
|
||||
cvar_t *png_compression;
|
||||
cvar_t *jpg_quality;
|
||||
|
||||
#define LERPBYTE(i) r = resamplerow1[i];out[i] = (byte)((((resamplerow2[i] - r) * lerp)>>16) + r)
|
||||
|
||||
|
@ -285,6 +286,7 @@ static const saveformat_t save_xash048[] =
|
|||
static const saveformat_t save_xash051[] =
|
||||
{
|
||||
{ "%s%s.%s", "tga", Image_SaveTGA }, // tga screenshots
|
||||
{ "%s%s.%s", "jpg", Image_SaveJPG }, // tga levelshots or screenshots
|
||||
{ "%s%s.%s", "png", Image_SavePNG }, // png levelshots
|
||||
{ "%s%s.%s", "dds", Image_SaveDDS }, // dds envshots
|
||||
{ NULL, NULL, NULL }
|
||||
|
@ -298,7 +300,9 @@ void Image_Init( void )
|
|||
gl_round_down = Cvar_Get( "gl_round_down", "0", CVAR_SYSTEMINFO, "down size non-power of two textures" );
|
||||
fs_textures = Cvar_Get( "fs_textures_path", "textures", CVAR_SYSTEMINFO, "textures default folder" );
|
||||
png_compression = Cvar_Get( "png_compression", "9", CVAR_SYSTEMINFO, "pnglib compression level" );
|
||||
jpg_quality = Cvar_Get( "jpg_quality", "7", CVAR_SYSTEMINFO, "jpglib quality level" );
|
||||
Cvar_SetValue( "png_compression", bound( 0, png_compression->integer, 9 ));
|
||||
Cvar_SetValue( "jpg_quality", bound( 0, jpg_quality->integer, 10 ));
|
||||
|
||||
// install image formats (can be re-install later by Image_Setup)
|
||||
switch( Sys.app_name )
|
||||
|
|
Binary file not shown.
|
@ -54,7 +54,7 @@ BSC32=bscmake.exe
|
|||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /opt:nowin98
|
||||
# ADD LINK32 zlib.lib png.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /dll /pdb:none /machine:I386 /nodefaultlib:"libc.lib" /libpath:"./imagelib" /opt:nowin98
|
||||
# ADD LINK32 zlib.lib png.lib jpg.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /dll /pdb:none /machine:I386 /nodefaultlib:"libc.lib" /libpath:"./imagelib" /opt:nowin98
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\launch\!release
|
||||
InputPath=\Xash3D\src_main\temp\launch\!release\launch.dll
|
||||
|
@ -90,7 +90,7 @@ BSC32=bscmake.exe
|
|||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 zlib.lib png.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"libc.lib" /pdbtype:sept /libpath:"./imagelib"
|
||||
# ADD LINK32 zlib.lib png.lib jpg.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"libc.lib" /pdbtype:sept /libpath:"./imagelib"
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\launch\!debug
|
||||
InputPath=\Xash3D\src_main\temp\launch\!debug\launch.dll
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<html>
|
||||
<body>
|
||||
<pre>
|
||||
<h1>Build Log</h1>
|
||||
<h3>
|
||||
--------------------Configuration: launch - Win32 Debug--------------------
|
||||
</h3>
|
||||
<h3>Command Lines</h3>
|
||||
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP75C.tmp" with contents
|
||||
[
|
||||
/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "imagelib" /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\launch\!debug/" /Fo"..\temp\launch\!debug/" /Fd"..\temp\launch\!debug/" /FD /GZ /c
|
||||
"D:\Xash3D\src_main\launch\imagelib\img_main.c"
|
||||
]
|
||||
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP75C.tmp"
|
||||
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP75D.tmp" with contents
|
||||
[
|
||||
zlib.lib png.lib jpg.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /dll /incremental:yes /pdb:"..\temp\launch\!debug/launch.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /out:"..\temp\launch\!debug/launch.dll" /implib:"..\temp\launch\!debug/launch.lib" /pdbtype:sept /libpath:"./imagelib"
|
||||
"\Xash3D\src_main\temp\launch\!debug\cmd.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\console.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\cpuinfo.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\crclib.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\cvar.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\export.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\filesystem.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_bmp.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_dds.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_jpg.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_main.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_pcx.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_png.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_tga.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_utils.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_vtf.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\img_wad.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\memlib.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\network.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\parselib.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\patch.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\stdlib.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\system.obj"
|
||||
"\Xash3D\src_main\temp\launch\!debug\utils.obj"
|
||||
]
|
||||
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP75D.tmp"
|
||||
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP75E.bat" with contents
|
||||
[
|
||||
@echo off
|
||||
copy \Xash3D\src_main\temp\launch\!debug\launch.dll "D:\Xash3D\bin\launch.dll"
|
||||
]
|
||||
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP75E.bat"
|
||||
Compiling...
|
||||
img_main.c
|
||||
Linking...
|
||||
<h3>Output Window</h3>
|
||||
Performing Custom Build Step on \Xash3D\src_main\temp\launch\!debug\launch.dll
|
||||
‘Ş®Ż¨ŕ®˘ ® ä ©«®˘: 1.
|
||||
|
||||
|
||||
|
||||
<h3>Results</h3>
|
||||
launch.dll - 0 error(s), 0 warning(s)
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1355,9 +1355,9 @@ void CM_CollisionClipTrace_BrushBox( trace_t *trace, const vec3_t cmins, const v
|
|||
VectorAdd( end, mins, endmins );
|
||||
VectorAdd( end, maxs, endmaxs );
|
||||
|
||||
boxbrush = CM_CollisionBrushForBox( identitymatrix, cmins, cmaxs, supercontents, surfaceflags, surface );
|
||||
thisbrush_start = CM_CollisionBrushForBox( identitymatrix, startmins, startmaxs, 0, 0, NULL );
|
||||
thisbrush_end = CM_CollisionBrushForBox( identitymatrix, endmins, endmaxs, 0, 0, NULL );
|
||||
boxbrush = CM_CollisionBrushForBox( matrix4x4_identity, cmins, cmaxs, supercontents, surfaceflags, surface );
|
||||
thisbrush_start = CM_CollisionBrushForBox( matrix4x4_identity, startmins, startmaxs, 0, 0, NULL );
|
||||
thisbrush_end = CM_CollisionBrushForBox( matrix4x4_identity, endmins, endmaxs, 0, 0, NULL );
|
||||
|
||||
Mem_Set( trace, 0, sizeof(trace_t));
|
||||
trace->contentsmask = hitsupercontentsmask;
|
||||
|
|
|
@ -275,8 +275,8 @@ void CM_TraceBmodel( const vec3_t start, const vec3_t end, const vec3_t mins, co
|
|||
VectorAdd(end, mins, boxendmins);
|
||||
VectorAdd(end, maxs, boxendmaxs);
|
||||
|
||||
thisbrush_start = CM_CollisionBrushForBox( identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL );
|
||||
thisbrush_end = CM_CollisionBrushForBox( identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL );
|
||||
thisbrush_start = CM_CollisionBrushForBox( matrix4x4_identity, boxstartmins, boxstartmaxs, 0, 0, NULL );
|
||||
thisbrush_end = CM_CollisionBrushForBox( matrix4x4_identity, boxendmins, boxendmaxs, 0, 0, NULL );
|
||||
|
||||
if( model && model->type == mod_brush )
|
||||
{
|
||||
|
@ -333,8 +333,8 @@ void CM_TraceStudio( const vec3_t start, const vec3_t end, const vec3_t mins, co
|
|||
VectorAdd(end, mins, boxendmins);
|
||||
VectorAdd(end, maxs, boxendmaxs);
|
||||
|
||||
thisbrush_start = CM_CollisionBrushForBox( identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL );
|
||||
thisbrush_end = CM_CollisionBrushForBox( identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL );
|
||||
thisbrush_start = CM_CollisionBrushForBox( matrix4x4_identity, boxstartmins, boxstartmaxs, 0, 0, NULL );
|
||||
thisbrush_end = CM_CollisionBrushForBox( matrix4x4_identity, boxendmins, boxendmaxs, 0, 0, NULL );
|
||||
CM_CollisionTraceBrushTriangleMeshFloat( trace, thisbrush_start, thisbrush_end, mod->col[0]->numtris, mod->col[0]->indices, (const float *)mod->col[0]->verts, CONTENTS_SOLID, 0, NULL, segmentmins, segmentmaxs );
|
||||
}
|
||||
}
|
|
@ -106,6 +106,9 @@ _inline float rsqrt( float number )
|
|||
int i;
|
||||
float x, y;
|
||||
|
||||
if( number == 0.0f )
|
||||
return 0.0f;
|
||||
|
||||
x = number * 0.5f;
|
||||
i = *(int *)&number; // evil floating point bit level hacking
|
||||
i = 0x5f3759df - (i >> 1); // what the fuck?
|
||||
|
@ -792,7 +795,6 @@ _inline int NearestPOW( int value, bool roundDown )
|
|||
return n;
|
||||
}
|
||||
|
||||
static vec3_t axis_identity[3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
|
||||
static quat_t quat_identity = { 0, 0, 0, 1 };
|
||||
static vec3_t vec3_origin = { 0, 0, 0 };
|
||||
static vec3_t vec3_angles = { 0, 0, 0 };
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#define MATRIX_LIB_H
|
||||
|
||||
//#define OPENGL_STYLE // TODO: enable OpenGL style someday
|
||||
//#define RHAND_STYLE // right hand style euler engles
|
||||
|
||||
/*
|
||||
Quake engine tranformation matrix
|
||||
|
@ -130,7 +129,7 @@ _inline void Matrix3x3_FromMatrix4x4( matrix3x3 out, const matrix4x4 in )
|
|||
#endif
|
||||
}
|
||||
|
||||
_inline void Matrix3x3_ToAngles( const matrix3x3 matrix, vec3_t out )
|
||||
_inline void Matrix3x3_ToAngles( const matrix3x3 matrix, vec3_t out, bool rhand )
|
||||
{
|
||||
double pitch, cpitch, yaw, roll;
|
||||
|
||||
|
@ -141,22 +140,16 @@ _inline void Matrix3x3_ToAngles( const matrix3x3 matrix, vec3_t out )
|
|||
{
|
||||
cpitch = 1.0f / cpitch;
|
||||
pitch = RAD2DEG( pitch );
|
||||
#ifdef RHAND_STYLE
|
||||
yaw = RAD2DEG( com.atan2( matrix[0][1] * cpitch, matrix[0][0] * cpitch ));
|
||||
#else
|
||||
yaw = RAD2DEG( com.atan2((-1)*-matrix[0][1] * cpitch, matrix[0][0] * cpitch ));
|
||||
#endif
|
||||
if( rhand ) yaw = RAD2DEG( com.atan2( matrix[0][1] * cpitch, matrix[0][0] * cpitch ));
|
||||
else yaw = RAD2DEG( com.atan2((-1)*-matrix[0][1] * cpitch, matrix[0][0] * cpitch ));
|
||||
roll = RAD2DEG( com.atan2( -matrix[1][2] * cpitch, matrix[2][2] * cpitch ));
|
||||
}
|
||||
else
|
||||
{
|
||||
pitch = matrix[0][2] > 0 ? -90.0f : 90.0f;
|
||||
yaw = RAD2DEG( atan2( matrix[1][0], -matrix[1][1] ));
|
||||
#ifdef RHAND_STYLE
|
||||
roll = 180;
|
||||
#else
|
||||
roll = 0;
|
||||
#endif
|
||||
if( rhand ) roll = 180;
|
||||
else roll = 0;
|
||||
}
|
||||
|
||||
out[PITCH] = pitch;
|
||||
|
@ -283,9 +276,9 @@ _inline void Matrix3x3_ConcatRotate( matrix3x3 out, double angle, double x, doub
|
|||
|
||||
========================================================================
|
||||
*/
|
||||
#define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, identitymatrix )
|
||||
#define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, matrix4x4_identity )
|
||||
|
||||
static const matrix4x4 identitymatrix =
|
||||
static const matrix4x4 matrix4x4_identity =
|
||||
{
|
||||
{ 1, 0, 0, 0 }, // PITCH
|
||||
{ 0, 1, 0, 0 }, // YAW
|
||||
|
@ -304,7 +297,7 @@ static const matrix4x4 matrix4x4_halfidentity =
|
|||
_inline void Matrix4x4_Copy( matrix4x4 out, const matrix4x4 in )
|
||||
{
|
||||
// FIXME: replace with Mem_Copy
|
||||
memcpy( out, in, sizeof(matrix4x4));
|
||||
memcpy( out, in, sizeof( matrix4x4 ));
|
||||
}
|
||||
|
||||
_inline void Matrix4x4_TransformPoint( const matrix4x4 in, vec3_t point )
|
||||
|
|
165
public/quatlib.h
165
public/quatlib.h
|
@ -5,29 +5,10 @@
|
|||
#ifndef QUATLIB_H
|
||||
#define QUATLIB_H
|
||||
|
||||
// FIXME: move out to mathlib.h
|
||||
_inline float Q_RSqrt( float number )
|
||||
{
|
||||
int i;
|
||||
float x2, y;
|
||||
|
||||
if( number == 0.0f )
|
||||
return 0.0f;
|
||||
|
||||
x2 = number * 0.5f;
|
||||
y = number;
|
||||
i = * (int *) &y; // evil floating point bit level hacking
|
||||
i = 0x5f3759df - (i >> 1); // what the fuck?
|
||||
y = * (float *) &i;
|
||||
y = y * (1.5f - (x2 * y * y)); // this can be done a second time
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
// The expression a * rsqrt(b) is intended as a higher performance alternative to a / sqrt(b).
|
||||
// The two expressions are comparably accurate, but do not compute exactly the same value in every case.
|
||||
// For example, a * rsqrt(a*a + b*b) can be just slightly greater than 1, in rare cases.
|
||||
#define SQRTFAST( x ) (( x ) * Q_RSqrt( x ))
|
||||
#define SQRTFAST( x ) (( x ) * rsqrt( x ))
|
||||
#define VectorLengthFast(v) (SQRTFAST(DotProduct((v),(v))))
|
||||
#define DistanceFast(v1,v2) (SQRTFAST(VectorDistance2(v1,v2)))
|
||||
|
||||
|
@ -85,7 +66,7 @@ _inline float Quat_Inverse( const quat_t q1, quat_t q2 )
|
|||
return Quat_Normalize( q2 );
|
||||
}
|
||||
|
||||
_inline void Matrix_Quat( vec3_t m[3], quat_t q )
|
||||
_inline void Quat_FromAxis( vec3_t m[3], quat_t q )
|
||||
{
|
||||
float tr, s;
|
||||
|
||||
|
@ -153,7 +134,7 @@ _inline void Quat_Lerp( const quat_t q1, const quat_t q2, float t, quat_t out )
|
|||
if( cosom < 1.0 - 0.0001 )
|
||||
{
|
||||
sinsqr = 1.0 - cosom * cosom;
|
||||
sinom = Q_RSqrt( sinsqr );
|
||||
sinom = rsqrt( sinsqr );
|
||||
omega = atan2( sinsqr * sinom, cosom );
|
||||
scale0 = sin( (1.0 - t) * omega ) * sinom;
|
||||
scale1 = sin( t * omega ) * sinom;
|
||||
|
@ -216,144 +197,4 @@ _inline void Quat_ConcatTransforms( const quat_t q1,const vec3_t v1,const quat_t
|
|||
v[1] += v1[1];
|
||||
v[2] += v1[2];
|
||||
}
|
||||
|
||||
_inline void Matrix_Identity( vec3_t m[3] )
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
if( i == j )
|
||||
m[i][j] = 1.0f;
|
||||
else m[i][j] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_inline void Matrix_Copy( vec3_t m1[3], vec3_t m2[3] )
|
||||
{
|
||||
Mem_Copy( m2, m1, sizeof( m2 ));
|
||||
}
|
||||
|
||||
_inline bool Matrix_Compare( vec3_t m1[3], vec3_t m2[3] )
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
for( j = 0; j < 3; j++ )
|
||||
if( m1[i][j] != m2[i][j] )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
_inline void Matrix_Multiply( vec3_t m1[3], vec3_t m2[3], vec3_t out[3] )
|
||||
{
|
||||
out[0][0] = m1[0][0]*m2[0][0] + m1[0][1]*m2[1][0] + m1[0][2]*m2[2][0];
|
||||
out[0][1] = m1[0][0]*m2[0][1] + m1[0][1]*m2[1][1] + m1[0][2]*m2[2][1];
|
||||
out[0][2] = m1[0][0]*m2[0][2] + m1[0][1]*m2[1][2] + m1[0][2]*m2[2][2];
|
||||
out[1][0] = m1[1][0]*m2[0][0] + m1[1][1]*m2[1][0] + m1[1][2]*m2[2][0];
|
||||
out[1][1] = m1[1][0]*m2[0][1] + m1[1][1]*m2[1][1] + m1[1][2]*m2[2][1];
|
||||
out[1][2] = m1[1][0]*m2[0][2] + m1[1][1]*m2[1][2] + m1[1][2]*m2[2][2];
|
||||
out[2][0] = m1[2][0]*m2[0][0] + m1[2][1]*m2[1][0] + m1[2][2]*m2[2][0];
|
||||
out[2][1] = m1[2][0]*m2[0][1] + m1[2][1]*m2[1][1] + m1[2][2]*m2[2][1];
|
||||
out[2][2] = m1[2][0]*m2[0][2] + m1[2][1]*m2[1][2] + m1[2][2]*m2[2][2];
|
||||
}
|
||||
|
||||
_inline void Matrix_TransformVector( vec3_t m[3], vec3_t v, vec3_t out )
|
||||
{
|
||||
out[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2];
|
||||
out[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2];
|
||||
out[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2];
|
||||
}
|
||||
|
||||
_inline void Matrix_Transpose( vec3_t in[3], vec3_t out[3] )
|
||||
{
|
||||
out[0][0] = in[0][0];
|
||||
out[1][1] = in[1][1];
|
||||
out[2][2] = in[2][2];
|
||||
|
||||
out[0][1] = in[1][0];
|
||||
out[0][2] = in[2][0];
|
||||
out[1][0] = in[0][1];
|
||||
out[1][2] = in[2][1];
|
||||
out[2][0] = in[0][2];
|
||||
out[2][1] = in[1][2];
|
||||
}
|
||||
|
||||
_inline void Matrix_EulerAngles( vec3_t m[3], vec3_t angles )
|
||||
{
|
||||
float c;
|
||||
float pitch, yaw, roll;
|
||||
|
||||
pitch = -asin( m[0][2] );
|
||||
c = cos( pitch );
|
||||
if( fabs( c ) > 5*10e-6 ) // Gimball lock?
|
||||
{
|
||||
// no
|
||||
c = 1.0f / c;
|
||||
pitch = RAD2DEG( pitch );
|
||||
yaw = RAD2DEG( atan2( m[0][1] * c, m[0][0] * c ));
|
||||
roll = RAD2DEG( atan2( -m[1][2] * c, m[2][2] * c ));
|
||||
}
|
||||
else
|
||||
{ // yes
|
||||
pitch = m[0][2] > 0 ? -90 : 90;
|
||||
yaw = RAD2DEG( atan2 ( m[1][0], -m[1][1] ) );
|
||||
roll = 180;
|
||||
}
|
||||
|
||||
angles[PITCH] = pitch;
|
||||
angles[YAW] = yaw;
|
||||
angles[ROLL] = roll;
|
||||
}
|
||||
|
||||
_inline void Matrix_Rotate( vec3_t m[3], float angle, float x, float y, float z )
|
||||
{
|
||||
vec3_t t[3], b[3];
|
||||
float c = cos( DEG2RAD( angle ));
|
||||
float s = sin( DEG2RAD( angle ));
|
||||
float mc = 1 - c, t1, t2;
|
||||
|
||||
t[0][0] = (x * x * mc) + c;
|
||||
t[1][1] = (y * y * mc) + c;
|
||||
t[2][2] = (z * z * mc) + c;
|
||||
|
||||
t1 = y * x * mc;
|
||||
t2 = z * s;
|
||||
t[0][1] = t1 + t2;
|
||||
t[1][0] = t1 - t2;
|
||||
|
||||
t1 = x * z * mc;
|
||||
t2 = y * s;
|
||||
t[0][2] = t1 - t2;
|
||||
t[2][0] = t1 + t2;
|
||||
|
||||
t1 = y * z * mc;
|
||||
t2 = x * s;
|
||||
t[1][2] = t1 + t2;
|
||||
t[2][1] = t1 - t2;
|
||||
|
||||
Matrix_Copy( m, b );
|
||||
Matrix_Multiply( b, t, m );
|
||||
}
|
||||
|
||||
_inline void Matrix_FromPoints( vec3_t v1, vec3_t v2, vec3_t v3, vec3_t m[3] )
|
||||
{
|
||||
float d;
|
||||
|
||||
m[2][0] = (v1[1] - v2[1]) * (v3[2] - v2[2]) - (v1[2] - v2[2]) * (v3[1] - v2[1]);
|
||||
m[2][1] = (v1[2] - v2[2]) * (v3[0] - v2[0]) - (v1[0] - v2[0]) * (v3[2] - v2[2]);
|
||||
m[2][2] = (v1[0] - v2[0]) * (v3[1] - v2[1]) - (v1[1] - v2[1]) * (v3[0] - v2[0]);
|
||||
VectorNormalizeFast( m[2] );
|
||||
|
||||
// this rotate and negate guarantees a vector not colinear with the original
|
||||
VectorSet( m[1], m[2][2], -m[2][0], m[2][1] );
|
||||
d = -DotProduct( m[1], m[2] );
|
||||
VectorMA( m[1], d, m[2], m[1] );
|
||||
VectorNormalizeFast( m[1] );
|
||||
CrossProduct( m[1], m[2], m[0] );
|
||||
}
|
||||
|
||||
#endif//QUATLIB_H
|
|
@ -182,7 +182,7 @@ void Mod_AliasLoadModel( ref_model_t *mod, ref_model_t *parent, const void *buff
|
|||
pouttag->origin[j] = LittleFloat( pintag->origin[j] );
|
||||
}
|
||||
|
||||
Matrix_Quat( axis, pouttag->quat );
|
||||
Quat_FromAxis( axis, pouttag->quat );
|
||||
Quat_Normalize( pouttag->quat );
|
||||
|
||||
com.strncpy( pouttag->name, pintag->name, MD3_MAX_PATH );
|
||||
|
|
|
@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
#define FTABLE_SIZE_POW 10
|
||||
#define FTABLE_SIZE ( 1<<FTABLE_SIZE_POW )
|
||||
|
@ -653,8 +653,8 @@ void R_DeformVertices( void )
|
|||
|
||||
if( RI.currententity && (RI.currentmodel != r_worldmodel) )
|
||||
{
|
||||
Matrix_TransformVector( RI.currententity->axis, RI.vright, v_right );
|
||||
Matrix_TransformVector( RI.currententity->axis, RI.vup, v_up );
|
||||
Matrix3x3_Transform( RI.currententity->axis, RI.vright, v_right );
|
||||
Matrix3x3_Transform( RI.currententity->axis, RI.vup, v_up );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -781,7 +781,7 @@ void R_DeformVertices( void )
|
|||
|
||||
if( !len[long_axis] )
|
||||
break;
|
||||
len[long_axis] = Q_RSqrt( len[long_axis] );
|
||||
len[long_axis] = rsqrt( len[long_axis] );
|
||||
VectorScale( m0[long_axis], len[long_axis], axis );
|
||||
|
||||
if( DotProduct( m0[long_axis], m0[short_axis] ) )
|
||||
|
@ -796,7 +796,7 @@ void R_DeformVertices( void )
|
|||
{
|
||||
if( !len[short_axis] )
|
||||
break;
|
||||
len[short_axis] = Q_RSqrt( len[short_axis] );
|
||||
len[short_axis] = rsqrt( len[short_axis] );
|
||||
VectorScale( m0[short_axis], len[short_axis], m0[0] );
|
||||
VectorCopy( axis, m0[1] );
|
||||
CrossProduct( m0[0], m0[1], m0[2] );
|
||||
|
@ -805,11 +805,11 @@ void R_DeformVertices( void )
|
|||
for( j = 0; j < 3; j++ )
|
||||
rot_centre[j] = ( quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j] ) * 0.25;
|
||||
|
||||
if( RI.currententity && ( RI.currentmodel != r_worldmodel ) )
|
||||
if( RI.currententity && ( RI.currentmodel != r_worldmodel ))
|
||||
{
|
||||
VectorAdd( RI.currententity->origin, rot_centre, tv );
|
||||
VectorSubtract( RI.viewOrigin, tv, tmp );
|
||||
Matrix_TransformVector( RI.currententity->axis, tmp, tv );
|
||||
Matrix3x3_Transform( RI.currententity->axis, tmp, tv );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -825,13 +825,13 @@ void R_DeformVertices( void )
|
|||
VectorCopy( axis, m1[1] );
|
||||
CrossProduct( m1[1], m1[2], m1[0] );
|
||||
|
||||
Matrix_Transpose( m1, m2 );
|
||||
Matrix_Multiply( m2, m0, result );
|
||||
Matrix3x3_Transpose( m2, m1 );
|
||||
Matrix3x3_Concat( result, m2, m0 );
|
||||
|
||||
for( j = 0; j < 4; j++ )
|
||||
{
|
||||
VectorSubtract( quad[j], rot_centre, tv );
|
||||
Matrix_TransformVector( result, tv, quad[j] );
|
||||
Matrix3x3_Transform( result, tv, quad[j] );
|
||||
VectorAdd( rot_centre, quad[j], quad[j] );
|
||||
}
|
||||
}
|
||||
|
@ -847,12 +847,11 @@ void R_DeformVertices( void )
|
|||
if( r_backacc.numElems % 6 )
|
||||
break;
|
||||
|
||||
if( RI.currententity && ( RI.currentmodel != r_worldmodel ) )
|
||||
if( RI.currententity && ( RI.currentmodel != r_worldmodel ))
|
||||
Matrix4_Matrix( RI.modelviewMatrix, m1 );
|
||||
else
|
||||
Matrix4_Matrix( RI.worldviewMatrix, m1 );
|
||||
else Matrix4_Matrix( RI.worldviewMatrix, m1 );
|
||||
|
||||
Matrix_Transpose( m1, m2 );
|
||||
Matrix3x3_Transpose( m2, m1 );
|
||||
|
||||
for( k = 0; k < r_backacc.numElems; k += 6 )
|
||||
{
|
||||
|
@ -866,21 +865,19 @@ void R_DeformVertices( void )
|
|||
|
||||
if( !VectorCompare( quad[3], quad[0] ) &&
|
||||
!VectorCompare( quad[3], quad[1] ) &&
|
||||
!VectorCompare( quad[3], quad[2] ) )
|
||||
!VectorCompare( quad[3], quad[2] ))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Matrix_FromPoints( quad[0], quad[1], quad[2], m0 );
|
||||
Matrix_Multiply( m2, m0, result );
|
||||
Matrix3x3_FromPoints( quad[0], quad[1], quad[2], m0 );
|
||||
Matrix3x3_Concat( result, m2, m0 );
|
||||
|
||||
// hack a scale up to keep particles from disappearing
|
||||
scale = ( quad[0][0] - RI.viewOrigin[0] ) * RI.vpn[0] + ( quad[0][1] - RI.viewOrigin[1] ) * RI.vpn[1] + ( quad[0][2] - RI.viewOrigin[2] ) * RI.vpn[2];
|
||||
if( scale < 20 )
|
||||
scale = 1.5;
|
||||
else
|
||||
scale = 1.5 + scale * 0.006f;
|
||||
if( scale < 20 ) scale = 1.5;
|
||||
else scale = 1.5 + scale * 0.006f;
|
||||
|
||||
for( j = 0; j < 3; j++ )
|
||||
rot_centre[j] = ( quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j] ) * 0.25;
|
||||
|
@ -888,7 +885,7 @@ void R_DeformVertices( void )
|
|||
for( j = 0; j < 4; j++ )
|
||||
{
|
||||
VectorSubtract( quad[j], rot_centre, tv );
|
||||
Matrix_TransformVector( result, tv, quad[j] );
|
||||
Matrix3x3_Transform( result, tv, quad[j] );
|
||||
VectorMA( rot_centre, scale, quad[j], quad[j] );
|
||||
}
|
||||
}
|
||||
|
@ -941,10 +938,10 @@ static bool R_VertexTCBase( const ref_stage_t *pass, int unit, mat4x4_t matrix )
|
|||
if( glState.in2DMode )
|
||||
return true;
|
||||
|
||||
if( !( RI.params & RP_SHADOWMAPVIEW ) )
|
||||
if( !( RI.params & RP_SHADOWMAPVIEW ))
|
||||
{
|
||||
VectorSubtract( RI.viewOrigin, RI.currententity->origin, projection );
|
||||
Matrix_TransformVector( RI.currententity->axis, projection, transform );
|
||||
Matrix3x3_Transform( RI.currententity->axis, projection, transform );
|
||||
|
||||
outCoords = tUnitCoordsArray[unit][0];
|
||||
for( i = 0, n = normalsArray[0]; i < r_backacc.numVerts; i++, outCoords += 2, n += 4 )
|
||||
|
@ -953,14 +950,14 @@ static bool R_VertexTCBase( const ref_stage_t *pass, int unit, mat4x4_t matrix )
|
|||
VectorNormalizeFast( projection );
|
||||
|
||||
depth = DotProduct( n, projection ); depth += depth;
|
||||
outCoords[0] = 0.5 + ( n[1] * depth - projection[1] ) * 0.5;
|
||||
outCoords[1] = 0.5 - ( n[2] * depth - projection[2] ) * 0.5;
|
||||
outCoords[0] = 0.5 + ( n[1] * depth - projection[1] ) * 0.5f;
|
||||
outCoords[1] = 0.5 - ( n[2] * depth - projection[2] ) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
GL_DisableAllTexGens();
|
||||
|
||||
R_UpdateVertexBuffer( tr.tcoordBuffer[unit], tUnitCoordsArray, r_backacc.numVerts * sizeof( vec2_t ));
|
||||
R_UpdateVertexBuffer( tr.tcoordBuffer[unit], tUnitCoordsArray[unit], r_backacc.numVerts * sizeof( vec2_t ));
|
||||
pglTexCoordPointer( 2, GL_FLOAT, 0, tr.tcoordBuffer[unit]->pointer );
|
||||
return true;
|
||||
}
|
||||
|
@ -1043,7 +1040,7 @@ static bool R_VertexTCBase( const ref_stage_t *pass, int unit, mat4x4_t matrix )
|
|||
Matrix4_Identity( m );
|
||||
|
||||
// rotate direction
|
||||
Matrix_TransformVector( RI.currententity->axis, dir, &m[0] );
|
||||
Matrix3x3_Transform( RI.currententity->axis, dir, &m[0] );
|
||||
VectorNormalizeLength( &m[0] );
|
||||
|
||||
VectorVectors( &m[0], &m[4], &m[8] );
|
||||
|
@ -1718,8 +1715,8 @@ void R_ModifyColor( const ref_stage_t *pass )
|
|||
break;
|
||||
case ALPHAGEN_SPECULAR:
|
||||
VectorSubtract( RI.viewOrigin, RI.currententity->origin, t );
|
||||
if( !Matrix_Compare( RI.currententity->axis, axis_identity ))
|
||||
Matrix_TransformVector( RI.currententity->axis, t, v );
|
||||
if( !Matrix3x3_Compare( RI.currententity->axis, matrix3x3_identity ))
|
||||
Matrix3x3_Transform( RI.currententity->axis, t, v );
|
||||
else VectorCopy( t, v );
|
||||
|
||||
for( i = 0; i < r_backacc.numColors; i++, bArray += 4 )
|
||||
|
@ -1732,8 +1729,8 @@ void R_ModifyColor( const ref_stage_t *pass )
|
|||
}
|
||||
break;
|
||||
case ALPHAGEN_DOT:
|
||||
if( !Matrix_Compare( RI.currententity->axis, axis_identity ) )
|
||||
Matrix_TransformVector( RI.currententity->axis, RI.vpn, v );
|
||||
if( !Matrix3x3_Compare( RI.currententity->axis, matrix3x3_identity ))
|
||||
Matrix3x3_Transform( RI.currententity->axis, RI.vpn, v );
|
||||
else VectorCopy( RI.vpn, v );
|
||||
|
||||
for( i = 0; i < r_backacc.numColors; i++, bArray += 4 )
|
||||
|
@ -1743,10 +1740,9 @@ void R_ModifyColor( const ref_stage_t *pass )
|
|||
}
|
||||
break;
|
||||
case ALPHAGEN_ONE_MINUS_DOT:
|
||||
if( !Matrix_Compare( RI.currententity->axis, axis_identity ) )
|
||||
Matrix_TransformVector( RI.currententity->axis, RI.vpn, v );
|
||||
else
|
||||
VectorCopy( RI.vpn, v );
|
||||
if( !Matrix3x3_Compare( RI.currententity->axis, matrix3x3_identity ))
|
||||
Matrix3x3_Transform( RI.currententity->axis, RI.vpn, v );
|
||||
else VectorCopy( RI.vpn, v );
|
||||
|
||||
for( i = 0; i < r_backacc.numColors; i++, bArray += 4 )
|
||||
{
|
||||
|
@ -2253,7 +2249,7 @@ static void R_RenderMeshGLSL_Material( void )
|
|||
}
|
||||
|
||||
// rotate direction
|
||||
Matrix_TransformVector( RI.currententity->axis, temp, lightDir );
|
||||
Matrix3x3_Transform( RI.currententity->axis, temp, lightDir );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,9 @@ typedef struct
|
|||
texture_t *shadowmapTextures[MAX_SHADOWGROUPS];
|
||||
texture_t *lightmapTextures[MAX_TEXTURES];
|
||||
|
||||
// builtin shaders
|
||||
// utility shaders
|
||||
ref_shader_t *defaultShader; // generic black texture
|
||||
ref_shader_t *currentSkyShader; // ponter to sky shader for current map
|
||||
} ref_globals_t;
|
||||
|
||||
extern ref_globals_t tr;
|
||||
|
|
|
@ -12,6 +12,19 @@ static rgba_t pic_colors[4];
|
|||
static mesh_t pic_mesh = { 4, pic_xyz, pic_xyz, NULL, pic_st, { 0, 0, 0, 0 }, { pic_colors, pic_colors, pic_colors, pic_colors }, 6, NULL };
|
||||
meshbuffer_t pic_mbuffer;
|
||||
|
||||
/*
|
||||
===============
|
||||
R_DrawSetColor
|
||||
===============
|
||||
*/
|
||||
void R_DrawSetColor( const void *data )
|
||||
{
|
||||
float *color = (float *)data;
|
||||
|
||||
if( color ) Vector4Copy( color, glState.draw_color );
|
||||
else Vector4Set( glState.draw_color, 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
R_DrawStretchPic
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "r_local.h"
|
||||
#include "byteorder.h"
|
||||
#include "mathlib.h"
|
||||
#include "matrix_lib.h"
|
||||
#include "const.h"
|
||||
|
||||
#define TEXTURES_HASH_SIZE 64
|
||||
|
@ -28,24 +29,30 @@ static byte *r_texpool; // texture_t permanent chain
|
|||
static byte data2D[256*256*4];
|
||||
static rgbdata_t r_image; // generic pixelbuffer used for internal textures
|
||||
|
||||
const vec3_t r_cubeMapAngles[6] =
|
||||
typedef struct envmap_s
|
||||
{
|
||||
{ 0, 0, 90 }, // px
|
||||
{ 0, 180, -90 }, // nx
|
||||
{ 0, 90, 0 }, // py
|
||||
{ 0, 270, 180 }, // ny
|
||||
{ -90, 180, -90 }, // pz
|
||||
{ 90, 180, 90 }, // nz
|
||||
vec3_t angles;
|
||||
int flags;
|
||||
} envmap_t;
|
||||
|
||||
const envmap_t r_skyBoxInfo[6] =
|
||||
{
|
||||
{{ 0, 270, 180}, IMAGE_FLIP_X },
|
||||
{{ 0, 90, 180}, IMAGE_FLIP_X },
|
||||
{{ -90, 0, 180}, IMAGE_FLIP_X },
|
||||
{{ 90, 0, 180}, IMAGE_FLIP_X },
|
||||
{{ 0, 0, 180}, IMAGE_FLIP_X },
|
||||
{{ 0, 180, 180}, IMAGE_FLIP_X },
|
||||
};
|
||||
|
||||
const vec3_t r_skyBoxAngles[6] =
|
||||
const envmap_t r_envMapInfo[6] =
|
||||
{
|
||||
{ 0, 90, 180}, // ft
|
||||
{ 0, 270, 180}, // bk
|
||||
{ -90, 0, 180}, // up
|
||||
{ 90, 0, 180}, // dn
|
||||
{ 0, 0, 180}, // rt
|
||||
{ 0, 180, 180}, // lf
|
||||
{{ 0, 0, 90}, 0 },
|
||||
{{ 0, 180, -90}, 0 },
|
||||
{{ 0, 90, 0}, 0 },
|
||||
{{ 0, 270, 180}, 0 },
|
||||
{{-90, 180, -90}, 0 },
|
||||
{{ 90, 180, 90}, 0 }
|
||||
};
|
||||
|
||||
static struct
|
||||
|
@ -126,6 +133,14 @@ void GL_LoadTexMatrix( const mat4x4_t m )
|
|||
glState.texIdentityMatrix[glState.activeTMU] = false;
|
||||
}
|
||||
|
||||
void GL_LoadMatrix( matrix4x4 source )
|
||||
{
|
||||
GLfloat dest[16];
|
||||
|
||||
Matrix4x4_ToArrayFloatGL( source, dest );
|
||||
pglLoadMatrixf( dest );
|
||||
}
|
||||
|
||||
void GL_LoadIdentityTexMatrix( void )
|
||||
{
|
||||
if( glState.texIdentityMatrix[glState.activeTMU] )
|
||||
|
@ -3079,6 +3094,7 @@ bool VID_ScreenShot( const char *filename, bool levelshot )
|
|||
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
|
||||
r_shot->width = glState.width;
|
||||
r_shot->height = glState.height;
|
||||
r_shot->flags = IMAGE_HAS_COLOR;
|
||||
r_shot->type = PF_RGB_24;
|
||||
r_shot->size = r_shot->width * r_shot->height * PFDesc( r_shot->type )->bpp;
|
||||
r_shot->palette = NULL;
|
||||
|
@ -3106,11 +3122,11 @@ VID_CubemapShot
|
|||
*/
|
||||
bool VID_CubemapShot( const char *base, uint size, bool skyshot )
|
||||
{
|
||||
rgbdata_t *r_shot;
|
||||
rgbdata_t *r_shot, *r_side;
|
||||
byte *temp = NULL;
|
||||
byte *buffer = NULL;
|
||||
string basename;
|
||||
int i = 1, result;
|
||||
int i = 1, flags, result;
|
||||
|
||||
if( RI.refdef.onlyClientDraw || !r_worldmodel )
|
||||
return false;
|
||||
|
@ -3128,20 +3144,36 @@ bool VID_CubemapShot( const char *base, uint size, bool skyshot )
|
|||
// alloc space
|
||||
temp = Mem_Alloc( r_temppool, size * size * 3 );
|
||||
buffer = Mem_Alloc( r_temppool, size * size * 3 * 6 );
|
||||
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
|
||||
r_side = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
|
||||
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
if( skyshot ) R_DrawCubemapView( r_lastRefdef.vieworg, r_skyBoxAngles[i], size );
|
||||
else R_DrawCubemapView( r_lastRefdef.vieworg, r_cubeMapAngles[i], size );
|
||||
|
||||
if( skyshot )
|
||||
{
|
||||
R_DrawCubemapView( r_lastRefdef.vieworg, r_skyBoxInfo[i].angles, size );
|
||||
flags = r_skyBoxInfo[i].flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_DrawCubemapView( r_lastRefdef.vieworg, r_envMapInfo[i].angles, size );
|
||||
flags = r_envMapInfo[i].flags;
|
||||
}
|
||||
pglReadPixels( 0, glState.height - size, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp );
|
||||
Mem_Copy( buffer + (size * size * 3 * i), temp, size * size * 3 );
|
||||
r_side->flags = IMAGE_HAS_COLOR;
|
||||
r_side->width = r_side->height = size;
|
||||
r_side->type = PF_RGB_24;
|
||||
r_side->size = r_side->width * r_side->height * 3;
|
||||
r_side->depth = r_side->numMips = 1;
|
||||
r_side->buffer = temp;
|
||||
|
||||
if( flags ) Image_Process( &r_side, 0, 0, flags );
|
||||
Mem_Copy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 );
|
||||
}
|
||||
|
||||
RI.params &= ~RP_ENVVIEW;
|
||||
|
||||
r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t ));
|
||||
r_shot->flags |= IMAGE_CUBEMAP;
|
||||
r_shot->flags |= IMAGE_CUBEMAP|IMAGE_HAS_COLOR;
|
||||
r_shot->width = size;
|
||||
r_shot->height = size;
|
||||
r_shot->type = PF_RGB_24;
|
||||
|
@ -3155,11 +3187,11 @@ bool VID_CubemapShot( const char *base, uint size, bool skyshot )
|
|||
com.strncpy( basename, base, MAX_STRING );
|
||||
FS_StripExtension( basename );
|
||||
FS_DefaultExtension( basename, ".dds" );
|
||||
|
||||
|
||||
// write image as dds packet
|
||||
result = FS_SaveImage( basename, r_shot );
|
||||
FS_FreeImage( r_shot );
|
||||
Mem_Free( temp );
|
||||
FS_FreeImage( r_side );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
@ -68,7 +68,7 @@ void R_LightBounds( const vec3_t origin, float intensity, vec3_t mins, vec3_t ma
|
|||
R_AddSurfDlighbits
|
||||
=============
|
||||
*/
|
||||
unsigned int R_AddSurfDlighbits( msurface_t *surf, unsigned int dlightbits )
|
||||
uint R_AddSurfDlighbits( msurface_t *surf, unsigned int dlightbits )
|
||||
{
|
||||
unsigned int k, bit;
|
||||
dlight_t *lt;
|
||||
|
@ -99,7 +99,7 @@ unsigned int R_AddSurfDlighbits( msurface_t *surf, unsigned int dlightbits )
|
|||
R_AddDynamicLights
|
||||
=================
|
||||
*/
|
||||
void R_AddDynamicLights( unsigned int dlightbits, int state )
|
||||
void R_AddDynamicLights( uint dlightbits, int state )
|
||||
{
|
||||
unsigned int i, j, numTempElems;
|
||||
bool cullAway;
|
||||
|
@ -121,7 +121,7 @@ void R_AddDynamicLights( unsigned int dlightbits, int state )
|
|||
{
|
||||
GL_SelectTexture( i );
|
||||
GL_TexEnv( GL_MODULATE );
|
||||
GL_SetState( state | ( i ? 0 : GLSTATE_BLEND_MTEX ) );
|
||||
GL_SetState( state | ( i ? 0 : GLSTATE_BLEND_MTEX ));
|
||||
GL_SetTexCoordArrayMode( 0 );
|
||||
GL_EnableTexGen( GL_S, GL_OBJECT_LINEAR );
|
||||
GL_EnableTexGen( GL_T, GL_OBJECT_LINEAR );
|
||||
|
@ -146,33 +146,32 @@ void R_AddDynamicLights( unsigned int dlightbits, int state )
|
|||
continue; // not lit by this light
|
||||
|
||||
VectorSubtract( light->origin, RI.currententity->origin, dlorigin );
|
||||
if( !Matrix_Compare( RI.currententity->axis, axis_identity ) )
|
||||
if( !Matrix3x3_Compare( RI.currententity->axis, matrix3x3_identity ))
|
||||
{
|
||||
VectorCopy( dlorigin, tvec );
|
||||
Matrix_TransformVector( RI.currententity->axis, tvec, dlorigin );
|
||||
Matrix3x3_Transform( RI.currententity->axis, tvec, dlorigin );
|
||||
}
|
||||
|
||||
shader = light->shader;
|
||||
if( shader && ( shader->flags & SHADER_CULL_BACK ) )
|
||||
cullAway = true;
|
||||
else
|
||||
cullAway = false;
|
||||
else cullAway = false;
|
||||
|
||||
numTempElems = 0;
|
||||
if( cullAway )
|
||||
{
|
||||
for( j = 0; j < r_backacc.numElems; j += 3 )
|
||||
{
|
||||
v1 = ( float * )( vertsArray + elemsArray[j+0] );
|
||||
v2 = ( float * )( vertsArray + elemsArray[j+1] );
|
||||
v3 = ( float * )( vertsArray + elemsArray[j+2] );
|
||||
v1 = (float *)( vertsArray + elemsArray[j+0] );
|
||||
v2 = (float *)( vertsArray + elemsArray[j+1] );
|
||||
v3 = (float *)( vertsArray + elemsArray[j+2] );
|
||||
|
||||
normal[0] = ( v1[1] - v2[1] ) * ( v3[2] - v2[2] ) - ( v1[2] - v2[2] ) * ( v3[1] - v2[1] );
|
||||
normal[1] = ( v1[2] - v2[2] ) * ( v3[0] - v2[0] ) - ( v1[0] - v2[0] ) * ( v3[2] - v2[2] );
|
||||
normal[2] = ( v1[0] - v2[0] ) * ( v3[1] - v2[1] ) - ( v1[1] - v2[1] ) * ( v3[0] - v2[0] );
|
||||
dist = ( dlorigin[0] - v1[0] ) * normal[0] + ( dlorigin[1] - v1[1] ) * normal[1] + ( dlorigin[2] - v1[2] ) * normal[2];
|
||||
|
||||
if( dist <= 0 || dist * Q_RSqrt( DotProduct( normal, normal ) ) >= light->intensity )
|
||||
if( dist <= 0 || dist * rsqrt( DotProduct( normal, normal ) ) >= light->intensity )
|
||||
continue;
|
||||
|
||||
tempElemsArray[numTempElems++] = elemsArray[j+0];
|
||||
|
@ -217,15 +216,13 @@ void R_AddDynamicLights( unsigned int dlightbits, int state )
|
|||
{
|
||||
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
|
||||
pglDrawRangeElementsEXT( GL_TRIANGLES, 0, r_backacc.numVerts, numTempElems, GL_UNSIGNED_INT, tempElemsArray );
|
||||
else
|
||||
pglDrawElements( GL_TRIANGLES, numTempElems, GL_UNSIGNED_INT, tempElemsArray );
|
||||
else pglDrawElements( GL_TRIANGLES, numTempElems, GL_UNSIGNED_INT, tempElemsArray );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( GL_Support( R_DRAW_RANGEELEMENTS_EXT ))
|
||||
pglDrawRangeElementsEXT( GL_TRIANGLES, 0, r_backacc.numVerts, r_backacc.numElems, GL_UNSIGNED_INT, elemsArray );
|
||||
else
|
||||
pglDrawElements( GL_TRIANGLES, r_backacc.numElems, GL_UNSIGNED_INT, elemsArray );
|
||||
else pglDrawElements( GL_TRIANGLES, r_backacc.numElems, GL_UNSIGNED_INT, elemsArray );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,7 +504,7 @@ void R_LightForEntity( ref_entity_t *e, byte *bArray )
|
|||
}
|
||||
|
||||
// rotate direction
|
||||
Matrix_TransformVector( e->axis, temp, direction );
|
||||
Matrix3x3_Transform( e->axis, temp, direction );
|
||||
|
||||
// see if we are affected by dynamic lights
|
||||
dlightbits = 0;
|
||||
|
@ -566,7 +563,7 @@ void R_LightForEntity( ref_entity_t *e, byte *bArray )
|
|||
dir = lightDirs[lnum];
|
||||
|
||||
// rotate
|
||||
Matrix_TransformVector( e->axis, dir, dlorigin );
|
||||
Matrix3x3_Transform( e->axis, dir, dlorigin );
|
||||
intensity8 = dl->intensity * 8 * e->scale;
|
||||
|
||||
cArray = tempColorsArray[0];
|
||||
|
@ -578,7 +575,7 @@ void R_LightForEntity( ref_entity_t *e, byte *bArray )
|
|||
if( add > 0 )
|
||||
{
|
||||
dot = DotProduct( dir, dir );
|
||||
add *= ( intensity8 / dot ) *Q_RSqrt( dot );
|
||||
add *= ( intensity8 / dot ) * rsqrt( dot );
|
||||
VectorMA( cArray, add, dl->color, cArray );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -477,6 +477,7 @@ void R_ShutdownOcclusionQueries( void );
|
|||
//
|
||||
extern meshbuffer_t pic_mbuffer;
|
||||
|
||||
void R_DrawSetColor( const void *data );
|
||||
void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, shader_t shadernum );
|
||||
void R_DrawStretchRaw( int x, int y, int w, int h, int cols, int rows, const byte *data, bool redraw );
|
||||
void R_DrawSetParms( shader_t handle, kRenderMode_t rendermode, int frame );
|
||||
|
@ -489,6 +490,7 @@ void R_DrawFill( float x, float y, float w, float h );
|
|||
void GL_SelectTexture( GLenum tmu );
|
||||
void GL_Bind( GLenum tmu, texture_t *tex );
|
||||
void GL_TexEnv( GLenum mode );
|
||||
void GL_LoadMatrix( matrix4x4 source );
|
||||
void GL_LoadTexMatrix( const mat4x4_t m );
|
||||
void GL_LoadIdentityTexMatrix( void );
|
||||
void GL_EnableTexGen( int coord, int mode );
|
||||
|
@ -709,13 +711,14 @@ struct skinfile_s *R_RegisterSkinFile( const char *name );
|
|||
ref_shader_t *R_FindShaderForSkinFile( const struct skinfile_s *skinfile, const char *meshname );
|
||||
|
||||
//
|
||||
// r_warp.c
|
||||
// r_sky.c
|
||||
//
|
||||
skydome_t *R_CreateSkydome( byte *mempool, float skyheight, ref_shader_t **farboxShaders, ref_shader_t **nearboxShaders );
|
||||
void R_FreeSkydome( skydome_t *skydome );
|
||||
void R_ClearSkyBox( void );
|
||||
void R_DrawSky( ref_shader_t *shader );
|
||||
bool R_AddSkySurface( msurface_t *fa );
|
||||
skydome_t *R_CreateSkydome( byte *mempool, float skyheight, ref_shader_t **farboxShaders, ref_shader_t **nearboxShaders );
|
||||
void R_FreeSkydome( skydome_t *skydome );
|
||||
void R_ClearSkyBox( void );
|
||||
void R_DrawSky( ref_shader_t *shader );
|
||||
bool R_AddSkySurface( msurface_t *fa );
|
||||
ref_shader_t *R_SetupSky( const char *name );
|
||||
|
||||
//====================================================================
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include <stdio.h> // sscanf
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
render_imp_t ri;
|
||||
stdlib_api_t com;
|
||||
|
@ -277,26 +277,24 @@ R_TransformEntityBBox
|
|||
*/
|
||||
void R_TransformEntityBBox( ref_entity_t *e, vec3_t mins, vec3_t maxs, vec3_t bbox[8], bool local )
|
||||
{
|
||||
int i;
|
||||
vec3_t axis[3], tmp;
|
||||
int i;
|
||||
vec3_t axis[3], tmp;
|
||||
|
||||
if( e == r_worldent )
|
||||
local = false;
|
||||
if( local )
|
||||
Matrix_Transpose( e->axis, axis ); // switch row-column order
|
||||
if( e == r_worldent ) local = false;
|
||||
if( local ) Matrix3x3_Transpose( axis, e->axis ); // switch row-column order
|
||||
|
||||
// rotate local bounding box and compute the full bounding box
|
||||
for( i = 0; i < 8; i++ )
|
||||
{
|
||||
vec_t *corner = bbox[i];
|
||||
|
||||
corner[0] = ( ( i & 1 ) ? mins[0] : maxs[0] );
|
||||
corner[1] = ( ( i & 2 ) ? mins[1] : maxs[1] );
|
||||
corner[2] = ( ( i & 4 ) ? mins[2] : maxs[2] );
|
||||
corner[0] = (( i & 1 ) ? mins[0] : maxs[0] );
|
||||
corner[1] = (( i & 2 ) ? mins[1] : maxs[1] );
|
||||
corner[2] = (( i & 4 ) ? mins[2] : maxs[2] );
|
||||
|
||||
if( local )
|
||||
{
|
||||
Matrix_TransformVector( axis, corner, tmp );
|
||||
Matrix3x3_Transform( axis, corner, tmp );
|
||||
VectorAdd( tmp, e->origin, corner );
|
||||
}
|
||||
}
|
||||
|
@ -398,10 +396,9 @@ bool R_LerpTag( orientation_t *orient, const ref_model_t *mod, int oldframe, int
|
|||
return false;
|
||||
|
||||
VectorClear( orient->origin );
|
||||
Matrix_Identity( orient->axis );
|
||||
Matrix3x3_LoadIdentity( orient->axis );
|
||||
|
||||
if( !name )
|
||||
return false;
|
||||
if( !name ) return false;
|
||||
|
||||
if( mod->type == mod_alias )
|
||||
return R_AliasModelLerpTag( orient, mod->extradata, oldframe, frame, lerpfrac, name );
|
||||
|
@ -625,24 +622,21 @@ R_PushFlareSurf
|
|||
*/
|
||||
static void R_PushFlareSurf( const meshbuffer_t *mb )
|
||||
{
|
||||
int i;
|
||||
vec4_t color;
|
||||
vec3_t origin, point, v;
|
||||
float radius = r_flaresize->value, colorscale, depth;
|
||||
float up = radius, down = -radius, left = -radius, right = radius;
|
||||
mbrushmodel_t *bmodel = ( mbrushmodel_t * )RI.currentmodel->extradata;
|
||||
msurface_t *surf = &bmodel->surfaces[mb->infokey - 1];
|
||||
ref_shader_t *shader;
|
||||
int i;
|
||||
vec4_t color;
|
||||
vec3_t origin, point, v;
|
||||
float radius = r_flaresize->value, colorscale, depth;
|
||||
float up = radius, down = -radius, left = -radius, right = radius;
|
||||
mbrushmodel_t *bmodel = ( mbrushmodel_t * )RI.currentmodel->extradata;
|
||||
msurface_t *surf = &bmodel->surfaces[mb->infokey - 1];
|
||||
ref_shader_t *shader;
|
||||
|
||||
if( RI.currentmodel != r_worldmodel )
|
||||
{
|
||||
Matrix_TransformVector( RI.currententity->axis, surf->origin, origin );
|
||||
Matrix3x3_Transform( RI.currententity->axis, surf->origin, origin );
|
||||
VectorAdd( origin, RI.currententity->origin, origin );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( surf->origin, origin );
|
||||
}
|
||||
else VectorCopy( surf->origin, origin );
|
||||
R_TransformToScreen_Vec3( origin, v );
|
||||
|
||||
if( v[0] < RI.refdef.viewport[0] || v[0] > RI.refdef.viewport[0] + RI.refdef.viewport[2] )
|
||||
|
@ -650,9 +644,8 @@ static void R_PushFlareSurf( const meshbuffer_t *mb )
|
|||
if( v[1] < RI.refdef.viewport[1] || v[1] > RI.refdef.viewport[1] + RI.refdef.viewport[3] )
|
||||
return;
|
||||
|
||||
pglReadPixels( (int)( v[0] /* + 0.5f*/ ), (int)( v[1] /* + 0.5f*/ ), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
|
||||
if( depth + 1e-4 < v[2] )
|
||||
return; // occluded
|
||||
pglReadPixels((int)( v[0] ), (int)( v[1] ), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth );
|
||||
if( depth + 1e-4 < v[2] ) return; // occluded
|
||||
|
||||
VectorCopy( origin, origin );
|
||||
|
||||
|
@ -2169,6 +2162,8 @@ shader_t Mod_RegisterShader( const char *name, int shaderType )
|
|||
src = R_LoadShader( name, shaderType, false, 0, SHADER_INVALID );
|
||||
break;
|
||||
case SHADER_SKY:
|
||||
src = R_SetupSky( name );
|
||||
break;
|
||||
default:
|
||||
MsgDev( D_WARN, "Mod_RegisterShader: invalid shader type (%i)\n", shaderType );
|
||||
return 0;
|
||||
|
@ -2285,7 +2280,7 @@ bool R_AddGenericEntity( edict_t *pRefEntity, ref_entity_t *refent, int ed_type,
|
|||
// interpolate origin
|
||||
for( i = 0; i < 3; i++ )
|
||||
refent->origin[i] = LerpPoint( pRefEntity->v.oldorigin[i], pRefEntity->v.origin[i], lerpfrac );
|
||||
AngleVectorsFLU( refent->angles, refent->axis[0], refent->axis[1], refent->axis[2] );
|
||||
Matrix3x3_FromAngles( refent->angles, refent->axis );
|
||||
VectorClear( refent->origin2 );
|
||||
|
||||
if( refent->ent_type == ED_VIEWMODEL )
|
||||
|
@ -2351,7 +2346,7 @@ bool R_AddPortalEntity( edict_t *pRefEntity, ref_entity_t *refent, int ed_type,
|
|||
}
|
||||
|
||||
// calculate angles
|
||||
AngleVectorsFLU( refent->angles, refent->axis[0], refent->axis[1], refent->axis[2] );
|
||||
Matrix3x3_FromAngles( refent->angles, refent->axis );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2438,14 +2433,6 @@ bool R_AddDynamicLight( vec3_t org, vec3_t color, float intensity, shader_t hand
|
|||
return true;
|
||||
}
|
||||
|
||||
void GL_SetColor( const void *data )
|
||||
{
|
||||
float *color = (float *)data;
|
||||
|
||||
if( color ) Vector4Copy( color, glState.draw_color );
|
||||
else Vector4Set( glState.draw_color, 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
}
|
||||
|
||||
void R_LightForPoint( const vec3_t point, vec3_t ambientLight )
|
||||
{
|
||||
vec4_t ambient;
|
||||
|
@ -2492,7 +2479,7 @@ render_exp_t DLLEXPORT *CreateAPI(stdlib_api_t *input, render_imp_t *engfuncs )
|
|||
re.RenderFrame = R_RenderScene;
|
||||
re.EndFrame = R_EndFrame;
|
||||
|
||||
re.SetColor = GL_SetColor;
|
||||
re.SetColor = R_DrawSetColor;
|
||||
re.GetParms = R_DrawGetParms;
|
||||
re.SetParms = R_DrawSetParms;
|
||||
re.ScrShot = VID_ScreenShot;
|
||||
|
|
|
@ -104,25 +104,6 @@ void Matrix4_MultiplyFast( const mat4x4_t m1, const mat4x4_t m2, mat4x4_t out )
|
|||
out[15] = 1.0f;
|
||||
}
|
||||
|
||||
void Matrix_FromQuaternion( const quat_t q, mat4x4_t out )
|
||||
{
|
||||
vec_t wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
|
||||
|
||||
x2 = q[0] + q[0]; y2 = q[1] + q[1]; z2 = q[2] + q[2];
|
||||
|
||||
xx = q[0] * x2; yy = q[1] * y2; zz = q[2] * z2;
|
||||
out[0] = 1.0f - yy - zz; out[5] = 1.0f - xx - zz; out[10] = 1.0f - xx - yy;
|
||||
|
||||
yz = q[1] * z2; wx = q[3] * x2;
|
||||
out[9] = yz - wx; out[6] = yz + wx;
|
||||
|
||||
xy = q[0] * y2; wz = q[3] * z2;
|
||||
out[4] = xy - wz; out[1] = xy + wz;
|
||||
|
||||
xz = q[0] * z2; wy = q[3] * y2;
|
||||
out[8] = xz + wy; out[2] = xz - wy;
|
||||
}
|
||||
|
||||
void Matrix4_Rotate( mat4x4_t m, vec_t angle, vec_t x, vec_t y, vec_t z )
|
||||
{
|
||||
mat4x4_t t, b;
|
||||
|
|
|
@ -35,8 +35,6 @@ void Matrix4_Scale( mat4x4_t m, vec_t x, vec_t y, vec_t z );
|
|||
void Matrix4_Transpose( const mat4x4_t m, mat4x4_t out );
|
||||
void Matrix4_Matrix( const mat4x4_t in, vec3_t out[3] );
|
||||
void Matrix4_Multiply_Vector( const mat4x4_t m, const vec4_t v, vec4_t out );
|
||||
void Matrix_FromQuaternion( const quat_t q, mat4x4_t out );
|
||||
|
||||
void Matrix4_Copy2D( const mat4x4_t m1, mat4x4_t m2 );
|
||||
void Matrix4_Multiply2D( const mat4x4_t m1, const mat4x4_t m2, mat4x4_t out );
|
||||
void Matrix4_Scale2D( mat4x4_t m, vec_t x, vec_t y );
|
||||
|
|
|
@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
#define QSORT_MAX_STACKDEPTH 2048
|
||||
|
||||
|
@ -418,8 +418,9 @@ static void R_BatchMeshBuffer( const meshbuffer_t *mb, const meshbuffer_t *nextm
|
|||
MB_NUM2SHADER( mb->shaderkey, shader );
|
||||
|
||||
if( shader->flags & SHADER_SKYPARMS )
|
||||
{ // draw sky
|
||||
if( !( RI.params & RP_NOSKY ) )
|
||||
{
|
||||
// draw sky
|
||||
if(!( RI.params & RP_NOSKY ))
|
||||
R_DrawSky( shader );
|
||||
return;
|
||||
}
|
||||
|
@ -792,15 +793,15 @@ static vec3_t r_portal_mins, r_portal_maxs, r_portal_centre;
|
|||
|
||||
static bool R_AddPortalSurface( const meshbuffer_t *mb )
|
||||
{
|
||||
int i;
|
||||
float dist;
|
||||
ref_entity_t *ent;
|
||||
ref_shader_t *shader;
|
||||
msurface_t *surf;
|
||||
cplane_t plane, oplane;
|
||||
mesh_t *mesh;
|
||||
vec3_t mins, maxs, centre;
|
||||
vec3_t v[3], entity_rotation[3];
|
||||
int i;
|
||||
float dist;
|
||||
ref_entity_t *ent;
|
||||
ref_shader_t *shader;
|
||||
msurface_t *surf;
|
||||
cplane_t plane, oplane;
|
||||
mesh_t *mesh;
|
||||
vec3_t mins, maxs, centre;
|
||||
vec3_t v[3], entity_rotation[3];
|
||||
|
||||
if( !mb )
|
||||
{
|
||||
|
@ -812,8 +813,7 @@ static bool R_AddPortalSurface( const meshbuffer_t *mb )
|
|||
}
|
||||
|
||||
MB_NUM2ENTITY( mb->sortkey, ent );
|
||||
if( !ent->model )
|
||||
return false;
|
||||
if( !ent->model ) return false;
|
||||
|
||||
surf = mb->infokey > 0 ? &r_worldbrushmodel->surfaces[mb->infokey-1] : NULL;
|
||||
if( !surf || !( mesh = surf->mesh ) || !mesh->xyzArray )
|
||||
|
@ -828,14 +828,14 @@ static bool R_AddPortalSurface( const meshbuffer_t *mb )
|
|||
oplane.dist += DotProduct( ent->origin, oplane.normal );
|
||||
CategorizePlane( &oplane );
|
||||
|
||||
if( !Matrix_Compare( ent->axis, axis_identity ))
|
||||
if( !Matrix3x3_Compare( ent->axis, matrix3x3_identity ))
|
||||
{
|
||||
Matrix_Transpose( ent->axis, entity_rotation );
|
||||
Matrix_TransformVector( entity_rotation, mesh->xyzArray[mesh->elems[0]], v[0] );
|
||||
Matrix3x3_Transpose( entity_rotation, ent->axis );
|
||||
Matrix3x3_Transform( entity_rotation, mesh->xyzArray[mesh->elems[0]], v[0] );
|
||||
VectorMA( ent->origin, ent->scale, v[0], v[0] );
|
||||
Matrix_TransformVector( entity_rotation, mesh->xyzArray[mesh->elems[1]], v[1] );
|
||||
Matrix3x3_Transform( entity_rotation, mesh->xyzArray[mesh->elems[1]], v[1] );
|
||||
VectorMA( ent->origin, ent->scale, v[1], v[1] );
|
||||
Matrix_TransformVector( entity_rotation, mesh->xyzArray[mesh->elems[2]], v[2] );
|
||||
Matrix3x3_Transform( entity_rotation, mesh->xyzArray[mesh->elems[2]], v[2] );
|
||||
VectorMA( ent->origin, ent->scale, v[2], v[2] );
|
||||
PlaneFromPoints( v, &plane );
|
||||
CategorizePlane( &plane );
|
||||
|
@ -882,7 +882,7 @@ static bool R_AddPortalSurface( const meshbuffer_t *mb )
|
|||
}
|
||||
r_portal_shader = shader;
|
||||
|
||||
if( !Matrix_Compare( ent->axis, axis_identity ) )
|
||||
if( !Matrix3x3_Compare( ent->axis, matrix3x3_identity ))
|
||||
{
|
||||
r_portal_ent = ent;
|
||||
r_portal_plane = plane;
|
||||
|
@ -894,7 +894,7 @@ static bool R_AddPortalSurface( const meshbuffer_t *mb )
|
|||
|
||||
if( !VectorCompare( r_portal_plane.normal, vec3_origin ) && !( VectorCompare( plane.normal, r_portal_plane.normal ) && plane.dist == r_portal_plane.dist ) )
|
||||
{
|
||||
if( VectorDistance2( RI.viewOrigin, centre ) > VectorDistance2( RI.viewOrigin, r_portal_centre ) )
|
||||
if( VectorDistance2( RI.viewOrigin, centre ) > VectorDistance2( RI.viewOrigin, r_portal_centre ))
|
||||
return true;
|
||||
VectorClear( r_portal_plane.normal );
|
||||
ClearBounds( r_portal_mins, r_portal_maxs );
|
||||
|
@ -1046,7 +1046,7 @@ setup_and_render:
|
|||
VectorNormalize( M[i] );
|
||||
}
|
||||
|
||||
Matrix_EulerAngles( M, angles );
|
||||
Matrix3x3_ToAngles( M, angles, true );
|
||||
angles[ROLL] = -angles[ROLL];
|
||||
|
||||
RI.params = RP_MIRRORVIEW|RP_FLIPFRONTFACE;
|
||||
|
@ -1055,8 +1055,8 @@ setup_and_render:
|
|||
}
|
||||
else
|
||||
{
|
||||
vec3_t tvec;
|
||||
vec3_t A[3], B[3], C[3], rot[3];
|
||||
vec3_t tvec;
|
||||
vec3_t A[3], B[3], C[3], rot[3];
|
||||
|
||||
// build world-to-portal rotation matrix
|
||||
VectorNegate( portal_plane->normal, A[0] );
|
||||
|
@ -1065,21 +1065,19 @@ setup_and_render:
|
|||
// build portal_dest-to-world rotation matrix
|
||||
VectorCopy( ent->movedir, portal_plane->normal );
|
||||
NormalVectorToAxis( portal_plane->normal, B );
|
||||
Matrix_Transpose( B, C );
|
||||
Matrix3x3_Transpose( C, B );
|
||||
|
||||
// multiply to get world-to-world rotation matrix
|
||||
Matrix_Multiply( C, A, rot );
|
||||
Matrix3x3_Concat( rot, C, A );
|
||||
|
||||
// translate view origin
|
||||
VectorSubtract( RI.viewOrigin, ent->origin, tvec );
|
||||
Matrix_TransformVector( rot, tvec, origin );
|
||||
Matrix3x3_Transform( rot, tvec, origin );
|
||||
VectorAdd( origin, ent->origin2, origin );
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
Matrix_TransformVector( A, RI.viewAxis[i], rot[i] );
|
||||
Matrix_Multiply( ent->axis, rot, B );
|
||||
for( i = 0; i < 3; i++ )
|
||||
Matrix_TransformVector( C, B[i], A[i] );
|
||||
for( i = 0; i < 3; i++ ) Matrix3x3_Transform( A, RI.viewAxis[i], rot[i] );
|
||||
Matrix3x3_Concat( B, ent->axis, rot );
|
||||
for( i = 0; i < 3; i++ ) Matrix3x3_Transform( C, B[i], A[i] );
|
||||
|
||||
// set up portal_plane
|
||||
// VectorCopy( A[0], portal_plane->normal );
|
||||
|
@ -1087,7 +1085,7 @@ setup_and_render:
|
|||
CategorizePlane( portal_plane );
|
||||
|
||||
// calculate Euler angles for our rotation matrix
|
||||
Matrix_EulerAngles( A, angles );
|
||||
Matrix3x3_ToAngles( A, angles, true );
|
||||
|
||||
// for portals, vis data is taken from portal origin, not
|
||||
// view origin, because the view point moves around and
|
||||
|
|
|
@ -118,9 +118,9 @@ enum
|
|||
|
||||
typedef struct
|
||||
{
|
||||
mesh_t *meshes;
|
||||
vec2_t *sphereStCoords[5];
|
||||
vec2_t *linearStCoords[6];
|
||||
mesh_t *meshes;
|
||||
vec2_t *sphereStCoords[6];
|
||||
vec2_t *linearStCoords[6];
|
||||
|
||||
struct ref_shader_s *farboxShaders[6];
|
||||
struct ref_shader_s *nearboxShaders[6];
|
||||
|
|
|
@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include "stdio.h" // sscanf
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
#include "byteorder.h"
|
||||
|
||||
#define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f)))
|
||||
|
@ -808,7 +808,7 @@ static void Mod_LoadShaderrefs( const lump_t *l )
|
|||
if( l->filelen % sizeof( *in ) )
|
||||
Host_Error( "Mod_LoadShaderrefs: funny lump size in %s\n", loadmodel->name );
|
||||
count = l->filelen / sizeof( *in );
|
||||
out = Mod_Malloc( loadmodel, count*sizeof( *out ) );
|
||||
out = Mod_Malloc( loadmodel, count*sizeof( *out ));
|
||||
|
||||
loadmodel->shaders = Mod_Malloc( loadmodel, count * sizeof( ref_shader_t* ));
|
||||
loadmodel->numshaders = count;
|
||||
|
@ -824,7 +824,7 @@ static void Mod_LoadShaderrefs( const lump_t *l )
|
|||
com.strncpy( out->name, in->name, sizeof( out->name ) );
|
||||
out->flags = LittleLong( in->surfaceFlags );
|
||||
contents = LittleLong( in->contentFlags );
|
||||
if( contents & ( MASK_WATER|CONTENTS_FOG ) )
|
||||
if( contents & ( MASK_WATER|CONTENTS_FOG ))
|
||||
out->flags |= SURF_NOMARKS;
|
||||
out->shader = NULL;
|
||||
}
|
||||
|
@ -1124,13 +1124,17 @@ static _inline void Mod_LoadFaceCommon( const dsurfacer_t *in, msurface_t *out )
|
|||
if( ( shaderType == SHADER_VERTEX && ( shaderref->shader->flags & SHADER_HASLIGHTMAP ) &&
|
||||
( shaderref->shader->stages[0].flags & SHADERSTAGE_LIGHTMAP )))
|
||||
out->shader = R_LoadShader( shaderref->name, shaderType, false, 0, shaderref->shader->type );
|
||||
else
|
||||
out->shader = shaderref->shader;
|
||||
else out->shader = shaderref->shader;
|
||||
}
|
||||
|
||||
out->flags = shaderref->flags;
|
||||
R_DeformvBBoxForShader( out->shader, ebbox );
|
||||
if( tr.currentSkyShader == NULL && (out->flags & SURF_SKY || out->shader->flags & SHADER_SKYPARMS ))
|
||||
{
|
||||
// because sky shader may missing skyParms, but always has surfaceparm 'sky'
|
||||
tr.currentSkyShader = out->shader;
|
||||
}
|
||||
|
||||
R_DeformvBBoxForShader( out->shader, ebbox );
|
||||
fognum = LittleLong( in->fognum );
|
||||
if( fognum != -1 && ( fognum < loadbmodel->numfogs ) )
|
||||
{
|
||||
|
@ -2062,6 +2066,7 @@ void R_BeginRegistration( const char *mapname, const dvis_t *visData )
|
|||
// explicitly free the old map if different
|
||||
if( com.strcmp( r_models[0].name, fullname ))
|
||||
{
|
||||
tr.currentSkyShader = NULL; // invalidate sky shader
|
||||
Mod_FreeModel( &r_models[0] );
|
||||
R_NewMap ();
|
||||
}
|
||||
|
@ -2106,7 +2111,7 @@ void R_BeginRegistration( const char *mapname, const dvis_t *visData )
|
|||
r_worldent->rtype = RT_MODEL;
|
||||
r_worldent->ent_type = ED_NORMAL;
|
||||
r_worldent->renderamt = 255; // i'm hope we don't want to see semisolid world :)
|
||||
Matrix_Identity( r_worldent->axis );
|
||||
Matrix3x3_LoadIdentity( r_worldent->axis );
|
||||
Mod_UpdateShaders( r_worldmodel );
|
||||
|
||||
r_framecount = 1;
|
||||
|
@ -2118,6 +2123,12 @@ void R_EndRegistration( const char *skyname )
|
|||
int i;
|
||||
ref_model_t *mod;
|
||||
|
||||
if( skyname && com.strncmp( skyname, "<skybox>", 8 ))
|
||||
{
|
||||
// half-life or quake2 skybox-style
|
||||
R_SetupSky( skyname );
|
||||
}
|
||||
|
||||
for( i = 0, mod = r_models; i < r_nummodels; i++, mod++ )
|
||||
{
|
||||
if( !mod->name ) continue;
|
||||
|
|
|
@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
static mesh_t poly_mesh;
|
||||
|
||||
|
@ -766,7 +766,7 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_
|
|||
{
|
||||
mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata;
|
||||
vec3_t temp, start_l, end_l, axis[3];
|
||||
bool rotated = !Matrix_Compare( test->axis, axis_identity );
|
||||
bool rotated = !Matrix3x3_Compare( test->axis, matrix3x3_identity );
|
||||
|
||||
// transform
|
||||
VectorSubtract( start, test->origin, start_l );
|
||||
|
@ -774,9 +774,9 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_
|
|||
if( rotated )
|
||||
{
|
||||
VectorCopy( start_l, temp );
|
||||
Matrix_TransformVector( test->axis, temp, start_l );
|
||||
Matrix3x3_Transform( test->axis, temp, start_l );
|
||||
VectorCopy( end_l, temp );
|
||||
Matrix_TransformVector( test->axis, temp, end_l );
|
||||
Matrix3x3_Transform( test->axis, temp, end_l );
|
||||
}
|
||||
|
||||
VectorCopy( start_l, trace_start );
|
||||
|
@ -792,9 +792,9 @@ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_
|
|||
// transform back
|
||||
if( rotated && trace_fraction != 1 )
|
||||
{
|
||||
Matrix_Transpose( test->axis, axis );
|
||||
Matrix3x3_Transpose( axis, test->axis );
|
||||
VectorCopy( tr->plane.normal, temp );
|
||||
Matrix_TransformVector( axis, temp, trace_plane.normal );
|
||||
Matrix3x3_Transform( axis, temp, trace_plane.normal );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -611,6 +611,48 @@ static bool Shader_SkipConditionBlock( script_t *script )
|
|||
}
|
||||
|
||||
//===========================================================================
|
||||
static bool Shader_CheckSkybox( const char *name )
|
||||
{
|
||||
const char *skybox_ext[4] = { "tga", "jpg", "png", "dds" };
|
||||
int i, j, num_checked_sides;
|
||||
const char *sidename;
|
||||
string loadname;
|
||||
|
||||
com.strncpy( loadname, name, sizeof( loadname ));
|
||||
FS_StripExtension( loadname );
|
||||
if( loadname[com.strlen( loadname ) - 1] == '_' )
|
||||
loadname[com.strlen( loadname ) - 1] = '\0';
|
||||
|
||||
if( FS_FileExists( va( "%s.dds", loadname )))
|
||||
return true;
|
||||
|
||||
if( FS_FileExists( va( "%s_.dds", loadname )))
|
||||
return true;
|
||||
|
||||
// complex cubemap pack not found, search for skybox images
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
num_checked_sides = 0;
|
||||
for( j = 0; j < 6; j++ )
|
||||
{
|
||||
// build side name
|
||||
sidename = va( "%s_%s.%s", loadname, r_skyBoxSuffix[j], skybox_ext[i] );
|
||||
if( FS_FileExists( sidename )) num_checked_sides++;
|
||||
|
||||
}
|
||||
if( num_checked_sides == 6 )
|
||||
return true; // image exists
|
||||
for( j = 0; j < 6; j++ )
|
||||
{
|
||||
// build side name
|
||||
sidename = va( "%s%s.%s", loadname, r_skyBoxSuffix[j], skybox_ext[i] );
|
||||
if( FS_FileExists( sidename )) num_checked_sides++;
|
||||
}
|
||||
if( num_checked_sides == 6 )
|
||||
return true; // images exists
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool Shader_ParseSkySides( script_t *script, ref_shader_t *shader, ref_shader_t **shaders, bool farbox )
|
||||
{
|
||||
|
@ -640,6 +682,9 @@ static bool Shader_ParseSkySides( script_t *script, ref_shader_t *shader, ref_sh
|
|||
if( com.stricmp( tok.string, "-" ) && com.stricmp( tok.string, "full" ))
|
||||
{
|
||||
shaderType = ( farbox ? SHADER_FARBOX : SHADER_NEARBOX );
|
||||
if( tok.string[com.strlen( tok.string ) - 1] == '_' )
|
||||
tok.string[com.strlen( tok.string ) - 1] = '\0';
|
||||
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
com.snprintf( name, sizeof( name ), "%s_%s", tok.string, r_skyBoxSuffix[i] );
|
||||
|
@ -647,10 +692,24 @@ static bool Shader_ParseSkySides( script_t *script, ref_shader_t *shader, ref_sh
|
|||
if( !image ) break;
|
||||
shaders[i] = R_LoadShader( image->name, shaderType, true, image->flags, SHADER_INVALID );
|
||||
}
|
||||
|
||||
if( i == 6 ) return true;
|
||||
Mem_Set( shaders, 0, sizeof( ref_shader_t * ) * 6 );
|
||||
return false;
|
||||
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
com.snprintf( name, sizeof( name ), "%s%s", tok.string, r_skyBoxSuffix[i] );
|
||||
image = R_FindTexture( name, NULL, 0, TF_CLAMP|TF_NOMIPMAP|TF_SKYSIDE );
|
||||
if( !image ) break;
|
||||
shaders[i] = R_LoadShader( image->name, shaderType, true, image->flags, SHADER_INVALID );
|
||||
}
|
||||
if( i == 6 ) return true;
|
||||
|
||||
// create default skybox
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
image = tr.skyTexture;
|
||||
shaders[i] = R_LoadShader( image->name, shaderType, true, image->flags, SHADER_INVALID );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -995,6 +1054,31 @@ static bool Shader_FogParms( ref_shader_t *shader, ref_stage_t *pass, script_t *
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool Shader_SkyRotate( ref_shader_t *shader, ref_stage_t *pass, script_t *script )
|
||||
{
|
||||
VectorSet( shader->skyAxis, 0.0f, 0.0f, 1.0f );
|
||||
shader->skySpeed = 0.0f;
|
||||
|
||||
// clear dist is optionally parm
|
||||
if( !Com_ReadFloat( script, false, &shader->skySpeed ))
|
||||
{
|
||||
MsgDev( D_ERROR, "missing sky speed for 'skyRotate' in shader '%s'\n", shader->name );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !Shader_ParseVector( script, shader->skyAxis, 3 )) // skyAxis is optionally
|
||||
{
|
||||
VectorSet( shader->skyAxis, 0.0f, 0.0f, 1.0f );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( VectorIsNull( shader->skyAxis ))
|
||||
VectorSet( shader->skyAxis, 0.0f, 0.0f, 1.0f );
|
||||
VectorNormalize( shader->skyAxis );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Shader_Sort( ref_shader_t *shader, ref_stage_t *pass, script_t *script )
|
||||
{
|
||||
token_t tok;
|
||||
|
@ -1111,7 +1195,8 @@ static const ref_parsekey_t shaderkeys[] =
|
|||
{ "endif", Shader_Endif },
|
||||
{ "portal", Shader_Portal },
|
||||
{ "fogvars", Shader_FogParms }, // RTCW fog params
|
||||
{ "skyparms", Shader_SkyParms },
|
||||
{ "skyParms", Shader_SkyParms },
|
||||
{ "skyRotate", Shader_SkyRotate },
|
||||
{ "fogparms", Shader_FogParms },
|
||||
{ "tessSize", Shader_TessSize },
|
||||
{ "nopicmip", Shader_shaderNoPicMip },
|
||||
|
@ -2340,10 +2425,14 @@ void R_ShaderList_f( void )
|
|||
switch( shader->type )
|
||||
{
|
||||
case SHADER_SKY:
|
||||
case SHADER_FARBOX:
|
||||
case SHADER_NEARBOX:
|
||||
Msg( "sky " );
|
||||
break;
|
||||
case SHADER_FARBOX:
|
||||
Msg( "far " );
|
||||
break;
|
||||
case SHADER_NEARBOX:
|
||||
Msg( "near " );
|
||||
break;
|
||||
case SHADER_TEXTURE:
|
||||
Msg( "bsp " );
|
||||
break;
|
||||
|
@ -2468,6 +2557,7 @@ void R_InitShaders( void )
|
|||
}
|
||||
|
||||
// parse this file
|
||||
MsgDev( D_LOAD, "loading shaderfile '%s'\n", t->filenames[i] );
|
||||
Shader_ParseFile( script, t->filenames[i] );
|
||||
Com_CloseScript( script );
|
||||
}
|
||||
|
@ -2561,6 +2651,13 @@ void Shader_FreeShader( ref_shader_t *shader )
|
|||
handle = shader - r_shaders;
|
||||
if(( shader->flags & SHADER_SKYPARMS ) && shader->skyParms )
|
||||
{
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
if( shader->skyParms->farboxShaders[i] )
|
||||
Shader_FreeShader( shader->skyParms->farboxShaders[i] );
|
||||
if( shader->skyParms->nearboxShaders[i] )
|
||||
Shader_FreeShader( shader->skyParms->nearboxShaders[i] );
|
||||
}
|
||||
R_FreeSkydome( shader->skyParms );
|
||||
shader->skyParms = NULL;
|
||||
}
|
||||
|
@ -3151,6 +3248,8 @@ static ref_shader_t *Shader_CreateDefault( ref_shader_t *shader, int type, int a
|
|||
{
|
||||
ref_stage_t *pass;
|
||||
texture_t *materialImages[MAX_STAGE_TEXTURES];
|
||||
script_t *script;
|
||||
char *skyParms;
|
||||
uint i, hashKey;
|
||||
|
||||
// make a default shader
|
||||
|
@ -3379,6 +3478,17 @@ static ref_shader_t *Shader_CreateDefault( ref_shader_t *shader, int type, int a
|
|||
pass->tcgen = TCGEN_BASE;
|
||||
pass->num_textures++;
|
||||
break;
|
||||
case SHADER_SKY:
|
||||
shader->type = SHADER_SKY;
|
||||
shader->name = Shader_Malloc( length + 1 );
|
||||
strcpy( shader->name, shortname );
|
||||
// create simple sky parms, to do Shader_SkyParms parsing it properly
|
||||
skyParms = va( "%s - -", shortname );
|
||||
script = Com_OpenScript( "skybox", skyParms, com.strlen( skyParms ));
|
||||
Shader_SkyParms( shader, NULL, script );
|
||||
Com_Assert( shader->skyParms == NULL );
|
||||
Com_CloseScript( script );
|
||||
break;
|
||||
case SHADER_FARBOX:
|
||||
shader->type = SHADER_FARBOX;
|
||||
shader->features = MF_STCOORDS;
|
||||
|
@ -3408,7 +3518,7 @@ static ref_shader_t *Shader_CreateDefault( ref_shader_t *shader, int type, int a
|
|||
shader->stages = ( ref_stage_t * )( ( byte * )shader->name + length + 1 );
|
||||
pass = &shader->stages[0];
|
||||
pass->flags = SHADERSTAGE_NOCOLORARRAY|SHADERSTAGE_BLEND_DECAL;
|
||||
pass->glState = GLSTATE_ALPHAFUNC|GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA;
|
||||
pass->glState = GLSTATE_ALPHAFUNC|GLSTATE_SRCBLEND_DST_COLOR|GLSTATE_DSTBLEND_SRC_COLOR;
|
||||
pass->textures[0] = R_FindTexture( shortname, NULL, 0, addFlags|TF_CLAMP|TF_NOMIPMAP );
|
||||
pass->rgbGen.type = RGBGEN_IDENTITY_LIGHTING;
|
||||
pass->alphaGen.type = ALPHAGEN_IDENTITY;
|
||||
|
@ -3705,4 +3815,133 @@ void R_ShaderSetRenderMode( kRenderMode_t mode )
|
|||
void R_ShaderAddSpriteIntervals( float interval )
|
||||
{
|
||||
r_spriteFrequency += interval;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_SetupSky
|
||||
=================
|
||||
*/
|
||||
ref_shader_t *R_SetupSky( const char *name )
|
||||
{
|
||||
string loadname;
|
||||
bool shader_valid = false;
|
||||
bool force_default = false;
|
||||
int index;
|
||||
|
||||
if( name && name[0] )
|
||||
{
|
||||
ref_script_t *cache;
|
||||
ref_shader_t *shader;
|
||||
uint hashKey;
|
||||
|
||||
com.strncpy( loadname, name, sizeof( loadname ));
|
||||
|
||||
// make sure what new shader it's a skyShader and existing
|
||||
hashKey = Com_HashKey( loadname, SHADERS_HASH_SIZE );
|
||||
|
||||
for( shader = r_shadersHash[hashKey]; shader; shader = shader->nextHash )
|
||||
{
|
||||
if( !com.stricmp( shader->name, loadname ))
|
||||
break;
|
||||
}
|
||||
if( shader )
|
||||
{
|
||||
// already loaded, check parms
|
||||
if( shader->flags & SHADER_SKYPARMS && shader->skyParms )
|
||||
shader_valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache = Shader_GetCache( loadname, SHADER_INVALID, hashKey );
|
||||
if( cache )
|
||||
{
|
||||
script_t *script = Com_OpenScript( cache->name, cache->buffer, cache->size );
|
||||
token_t tok;
|
||||
|
||||
while( Com_ReadToken( script, SC_ALLOW_NEWLINES, &tok ))
|
||||
{
|
||||
if( !com.stricmp( "skyParms", tok.string ))
|
||||
{
|
||||
// check only far skybox images for existing
|
||||
// because near skybox without far will be ignored by engine
|
||||
if( Com_ReadToken( script, SC_ALLOW_PATHNAMES2, &tok ))
|
||||
{
|
||||
if( com.stricmp( "-", tok.string ))
|
||||
{
|
||||
if( Shader_CheckSkybox( tok.string ))
|
||||
{
|
||||
shader_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shader_valid = true;
|
||||
break; // new shader just reset skybox
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( !com.stricmp( "surfaceParm", tok.string ))
|
||||
{
|
||||
// check only far skybox images for existing
|
||||
// because near skybox without far will be ignored by engine
|
||||
if( Com_ReadToken( script, SC_ALLOW_PATHNAMES2, &tok ))
|
||||
{
|
||||
if( !com.stricmp( "sky", tok.string ))
|
||||
{
|
||||
shader_valid = true;
|
||||
break; // yes it's q3-style skyshader
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Com_CloseScript( script );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !Shader_CheckSkybox( loadname ))
|
||||
{
|
||||
com.strncpy( loadname, va( "env/%s", name ), sizeof( loadname ));
|
||||
if(!Shader_CheckSkybox( loadname ))
|
||||
{
|
||||
com.strncpy( loadname, va( "gfx/env/%s", name ), sizeof( loadname ));
|
||||
if( Shader_CheckSkybox( loadname ))
|
||||
shader_valid = true;
|
||||
}
|
||||
else shader_valid = true;
|
||||
}
|
||||
else shader_valid = true;
|
||||
force_default = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NULL names force to get current sky shader by user requesting
|
||||
return (tr.currentSkyShader) ? tr.currentSkyShader : tr.defaultShader;
|
||||
}
|
||||
|
||||
if( !shader_valid )
|
||||
{
|
||||
MsgDev( D_ERROR, "R_SetupSky: 'couldn't find shader '%s'\n", name );
|
||||
return (tr.currentSkyShader) ? tr.currentSkyShader : tr.defaultShader;
|
||||
}
|
||||
|
||||
if( tr.currentSkyShader == NULL )
|
||||
{
|
||||
if( !r_worldmodel ) MsgDev( D_ERROR, "R_SetupSky: map not loaded\n" );
|
||||
else MsgDev( D_ERROR, "R_SetupSky: map %s not contain sky surfaces\n", r_worldmodel->name );
|
||||
return (tr.currentSkyShader) ? tr.currentSkyShader : tr.defaultShader;
|
||||
}
|
||||
|
||||
index = tr.currentSkyShader->shadernum;
|
||||
Shader_FreeShader( tr.currentSkyShader ); // release old sky
|
||||
|
||||
// new sky shader
|
||||
tr.currentSkyShader = R_LoadShader( loadname, SHADER_SKY, force_default, 0, SHADER_INVALID );
|
||||
if( index != tr.currentSkyShader->shadernum )
|
||||
MsgDev( D_ERROR, "R_SetupSky: mismatch shader indexes %i != %i\n", index, tr.currentSkyShader->shadernum );
|
||||
|
||||
return tr.currentSkyShader;
|
||||
}
|
|
@ -325,6 +325,9 @@ typedef struct ref_shader_s
|
|||
deform_t *deforms;
|
||||
|
||||
skydome_t *skyParms;
|
||||
vec3_t skyAxis;
|
||||
float skySpeed;
|
||||
|
||||
rgba_t fog_color;
|
||||
float fog_dist;
|
||||
float fog_clearDist;
|
||||
|
|
|
@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
/*
|
||||
=============================================================
|
||||
|
@ -135,8 +135,8 @@ void R_DeformVPlanarShadow( int numV, float *v )
|
|||
|
||||
R_GetShadowImpactAndDir( e, &tr, lightdir );
|
||||
|
||||
Matrix_TransformVector( e->axis, lightdir, lightdir2 );
|
||||
Matrix_TransformVector( e->axis, tr.plane.normal, planenormal );
|
||||
Matrix3x3_Transform( e->axis, lightdir, lightdir2 );
|
||||
Matrix3x3_Transform( e->axis, tr.plane.normal, planenormal );
|
||||
VectorScale( planenormal, e->scale, planenormal );
|
||||
|
||||
VectorSubtract( tr.endpos, e->origin, point );
|
||||
|
@ -468,7 +468,7 @@ void R_DrawShadowmaps( void )
|
|||
VectorNormalizeFast( lightdir );
|
||||
|
||||
NormalVectorToAxis( lightdir, M );
|
||||
Matrix_EulerAngles( M, angles );
|
||||
Matrix3x3_ToAngles( M, angles, true );
|
||||
|
||||
for( j = 0; j < 3; j++ )
|
||||
RI.refdef.viewangles[j] = anglemod( angles[j] );
|
||||
|
|
205
render/r_sky.c
205
render/r_sky.c
|
@ -24,25 +24,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
|
||||
#define SIDE_SIZE 9
|
||||
#define POINTS_LEN ( SIDE_SIZE * SIDE_SIZE )
|
||||
#define ELEM_LEN (( SIDE_SIZE-1 ) * ( SIDE_SIZE-1 ) * 6 )
|
||||
#define MAX_CLIP_VERTS 64
|
||||
#define SIDE_SIZE 9
|
||||
#define POINTS_LEN ( SIDE_SIZE * SIDE_SIZE )
|
||||
#define ELEM_LEN (( SIDE_SIZE-1 ) * ( SIDE_SIZE-1 ) * 6 )
|
||||
|
||||
#define SPHERE_RAD 10.0f
|
||||
#define EYE_RAD 9.0f
|
||||
#define SPHERE_RAD 10.0f
|
||||
#define EYE_RAD 9.0f
|
||||
|
||||
#define SCALE_S 4.0f // arbitrary (?) texture scaling factors
|
||||
#define SCALE_T 4.0f
|
||||
#define SCALE_S 4.0f // arbitrary (?) texture scaling factors
|
||||
#define SCALE_T 4.0f
|
||||
#define ST_MIN 1.0f / 512f
|
||||
#define ST_MAX 511.0f / 512f
|
||||
|
||||
#define BOX_SIZE 1.0f
|
||||
#define BOX_STEP BOX_SIZE / ( SIDE_SIZE-1 ) * 2.0f
|
||||
#define BOX_SIZE 1.0f
|
||||
#define BOX_STEP BOX_SIZE / ( SIDE_SIZE-1 ) * 2.0f
|
||||
|
||||
elem_t r_skydome_elems[6][ELEM_LEN];
|
||||
meshbuffer_t r_skydome_mbuffer;
|
||||
elem_t r_skydome_elems[6][ELEM_LEN];
|
||||
meshbuffer_t r_skydome_mbuffer;
|
||||
|
||||
static mfog_t *r_skyfog;
|
||||
static msurface_t *r_warpface;
|
||||
static bool r_warpfacevis;
|
||||
static mfog_t *r_skyfog;
|
||||
static msurface_t *r_warpface;
|
||||
static bool r_warpfacevis;
|
||||
|
||||
static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow, vec3_t dcol, float skyheight );
|
||||
static void MakeSkyVec( float x, float y, float z, int axis, vec3_t v );
|
||||
|
@ -61,7 +64,7 @@ skydome_t *R_CreateSkydome( byte *mempool, float skyheight, ref_shader_t **farbo
|
|||
byte *buffer;
|
||||
|
||||
size = sizeof( skydome_t ) + sizeof( mesh_t ) * 6 + sizeof( vec4_t ) * POINTS_LEN * 6 +
|
||||
sizeof( vec4_t ) * POINTS_LEN * 6 + sizeof( vec2_t ) * POINTS_LEN * 11;
|
||||
sizeof( vec4_t ) * POINTS_LEN * 6 + sizeof( vec2_t ) * POINTS_LEN * 12;
|
||||
buffer = Mem_Alloc( mempool, size );
|
||||
|
||||
skydome = ( skydome_t * )buffer;
|
||||
|
@ -77,10 +80,7 @@ skydome_t *R_CreateSkydome( byte *mempool, float skyheight, ref_shader_t **farbo
|
|||
mesh->numVertexes = POINTS_LEN;
|
||||
mesh->xyzArray = ( vec4_t * )buffer; buffer += sizeof( vec4_t ) * POINTS_LEN;
|
||||
mesh->normalsArray = ( vec4_t * )buffer; buffer += sizeof( vec4_t ) * POINTS_LEN;
|
||||
if( i != 5 )
|
||||
{
|
||||
skydome->sphereStCoords[i] = ( vec2_t * )buffer; buffer += sizeof( vec2_t ) * POINTS_LEN;
|
||||
}
|
||||
skydome->sphereStCoords[i] = ( vec2_t * )buffer; buffer += sizeof( vec2_t ) * POINTS_LEN;
|
||||
skydome->linearStCoords[i] = ( vec2_t * )buffer; buffer += sizeof( vec2_t ) * POINTS_LEN;
|
||||
|
||||
mesh->numElems = ELEM_LEN;
|
||||
|
@ -134,10 +134,10 @@ through the box verts to the sphere to find the texture coordinates.
|
|||
*/
|
||||
static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow, vec3_t dcol, float skyheight )
|
||||
{
|
||||
vec3_t pos, w, row, norm;
|
||||
float *v, *n, *st = NULL, *st2;
|
||||
int r, c;
|
||||
float t, d, d2, b, b2, q[2], s;
|
||||
vec3_t pos, w, row, norm;
|
||||
float *v, *n, *st, *st2;
|
||||
float t, d, d2, b, b2, q[2], s;
|
||||
int r, c;
|
||||
|
||||
s = 1.0 / ( SIDE_SIZE-1 );
|
||||
d = EYE_RAD; // sphere center to camera distance
|
||||
|
@ -149,14 +149,10 @@ static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow,
|
|||
|
||||
v = skydome->meshes[side].xyzArray[0];
|
||||
n = skydome->meshes[side].normalsArray[0];
|
||||
if( side != 5 )
|
||||
st = skydome->sphereStCoords[side][0];
|
||||
st = skydome->sphereStCoords[side][0];
|
||||
st2 = skydome->linearStCoords[side][0];
|
||||
|
||||
VectorCopy( orig, row );
|
||||
|
||||
// CrossProduct( dcol, drow, norm );
|
||||
// VectorNormalize( norm );
|
||||
VectorClear( norm );
|
||||
|
||||
for( r = 0; r < SIDE_SIZE; r++ )
|
||||
|
@ -176,17 +172,14 @@ static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow,
|
|||
w[0] *= t;
|
||||
w[1] *= t;
|
||||
|
||||
if( st )
|
||||
{
|
||||
// use x and y on sphere as s and t
|
||||
// minus is here so skies scoll in correct (Q3A's) direction
|
||||
st[0] = -w[0] * q[0];
|
||||
st[1] = -w[1] * q[1];
|
||||
// use x and y on sphere as s and t
|
||||
// minus is here so skies scoll in correct (Q3A's) direction
|
||||
st[0] = -w[0] * q[0];
|
||||
st[1] = -w[1] * q[1];
|
||||
|
||||
// avoid bilerp seam
|
||||
st[0] = ( bound( -1, st[0], 1 ) + 1.0 ) * 0.5;
|
||||
st[1] = ( bound( -1, st[1], 1 ) + 1.0 ) * 0.5;
|
||||
}
|
||||
// avoid bilerp seam
|
||||
st[0] = ( bound( -1, st[0], 1 ) + 1.0 ) * 0.5;
|
||||
st[1] = ( bound( -1, st[1], 1 ) + 1.0 ) * 0.5;
|
||||
|
||||
st2[0] = c * s;
|
||||
st2[1] = 1.0 - r * s;
|
||||
|
@ -196,10 +189,9 @@ static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow,
|
|||
|
||||
v += 4;
|
||||
n += 4;
|
||||
if( st ) st += 2;
|
||||
st += 2;
|
||||
st2 += 2;
|
||||
}
|
||||
|
||||
VectorAdd( row, drow, row );
|
||||
}
|
||||
}
|
||||
|
@ -213,8 +205,7 @@ static void R_DrawSkySide( skydome_t *skydome, int side, ref_shader_t *shader, i
|
|||
{
|
||||
meshbuffer_t *mbuffer = &r_skydome_mbuffer;
|
||||
|
||||
if( RI.skyMins[0][side] >= RI.skyMaxs[0][side] ||
|
||||
RI.skyMins[1][side] >= RI.skyMaxs[1][side] )
|
||||
if( RI.skyMins[0][side] >= RI.skyMaxs[0][side] || RI.skyMins[1][side] >= RI.skyMaxs[1][side] )
|
||||
return;
|
||||
|
||||
mbuffer->shaderkey = shader->sortkey;
|
||||
|
@ -233,8 +224,8 @@ R_DrawSkyBox
|
|||
*/
|
||||
static void R_DrawSkyBox( skydome_t *skydome, ref_shader_t **shaders )
|
||||
{
|
||||
int i, features;
|
||||
const int skytexorder[6] = { SKYBOX_RIGHT, SKYBOX_FRONT, SKYBOX_LEFT, SKYBOX_BACK, SKYBOX_TOP, SKYBOX_BOTTOM };
|
||||
int i, features;
|
||||
const int skytexorder[6] = { SKYBOX_RIGHT, SKYBOX_FRONT, SKYBOX_LEFT, SKYBOX_BACK, SKYBOX_TOP, SKYBOX_BOTTOM };
|
||||
|
||||
features = shaders[0]->features;
|
||||
if( r_shownormals->integer )
|
||||
|
@ -272,11 +263,13 @@ void R_DrawSky( ref_shader_t *shader )
|
|||
vec3_t mins, maxs;
|
||||
mat4x4_t m, oldm;
|
||||
elem_t *elem;
|
||||
skydome_t *skydome = shader->skyParms ? shader->skyParms : NULL;
|
||||
skydome_t *skydome;
|
||||
meshbuffer_t *mbuffer = &r_skydome_mbuffer;
|
||||
int u, v, umin, umax, vmin, vmax;
|
||||
|
||||
if( !skydome ) return;
|
||||
if( !shader ) return;
|
||||
skydome = shader->skyParms ? shader->skyParms : NULL;
|
||||
if( !skydome) return;
|
||||
|
||||
ClearBounds( mins, maxs );
|
||||
for( i = 0; i < 6; i++ )
|
||||
|
@ -284,17 +277,17 @@ void R_DrawSky( ref_shader_t *shader )
|
|||
if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] )
|
||||
continue;
|
||||
|
||||
umin = (int)( ( RI.skyMins[0][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) );
|
||||
umax = (int)( ( RI.skyMaxs[0][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ) + 1;
|
||||
vmin = (int)( ( RI.skyMins[1][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) );
|
||||
vmax = (int)( ( RI.skyMaxs[1][i]+1.0f )*0.5f*(float)( SIDE_SIZE-1 ) ) + 1;
|
||||
umin = (int)(( RI.skyMins[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 ));
|
||||
umax = (int)(( RI.skyMaxs[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 )) + 1;
|
||||
vmin = (int)(( RI.skyMins[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 ));
|
||||
vmax = (int)(( RI.skyMaxs[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 )) + 1;
|
||||
|
||||
umin = bound( 0, umin, SIDE_SIZE-1 );
|
||||
umax = bound( 0, umax, SIDE_SIZE-1 );
|
||||
vmin = bound( 0, vmin, SIDE_SIZE-1 );
|
||||
vmax = bound( 0, vmax, SIDE_SIZE-1 );
|
||||
|
||||
// Box elems in tristrip order
|
||||
// box elems in tristrip order
|
||||
elem = skydome->meshes[i].elems;
|
||||
for( v = vmin; v < vmax; v++ )
|
||||
{
|
||||
|
@ -311,7 +304,7 @@ void R_DrawSky( ref_shader_t *shader )
|
|||
AddPointToBounds( skydome->meshes[i].xyzArray[vmin*SIDE_SIZE+umin], mins, maxs );
|
||||
AddPointToBounds( skydome->meshes[i].xyzArray[vmax*SIDE_SIZE+umax], mins, maxs );
|
||||
|
||||
skydome->meshes[i].numElems = ( vmax-vmin )*( umax-umin )*6;
|
||||
skydome->meshes[i].numElems = ( vmax-vmin )*( umax-umin ) * 6;
|
||||
}
|
||||
|
||||
VectorAdd( mins, RI.viewOrigin, mins );
|
||||
|
@ -327,9 +320,13 @@ void R_DrawSky( ref_shader_t *shader )
|
|||
Matrix4_Copy( RI.modelviewMatrix, oldm );
|
||||
Matrix4_Copy( RI.worldviewMatrix, RI.modelviewMatrix );
|
||||
Matrix4_Copy( RI.worldviewMatrix, m );
|
||||
m[12] = 0;
|
||||
m[13] = 0;
|
||||
m[14] = 0;
|
||||
|
||||
if( shader->skySpeed )
|
||||
{
|
||||
float angle = shader->skySpeed * RI.refdef.time;
|
||||
Matrix4_Rotate( m, angle, shader->skyAxis[0], shader->skyAxis[1], shader->skyAxis[2] );
|
||||
}
|
||||
m[12] = m[13] = m[14] = 0.0f;
|
||||
m[15] = 1.0;
|
||||
pglLoadMatrixf( m );
|
||||
|
||||
|
@ -342,13 +339,11 @@ void R_DrawSky( ref_shader_t *shader )
|
|||
|
||||
// it can happen that sky surfaces have no fog hull specified
|
||||
// yet there's a global fog hull (see wvwq3dm7)
|
||||
if( !r_skyfog )
|
||||
r_skyfog = r_worldbrushmodel->globalfog;
|
||||
if( !r_skyfog ) r_skyfog = r_worldbrushmodel->globalfog;
|
||||
|
||||
if( skydome->farboxShaders[0] )
|
||||
R_DrawSkyBox( skydome, skydome->farboxShaders );
|
||||
else
|
||||
R_DrawBlackBottom( skydome );
|
||||
else R_DrawBlackBottom( skydome );
|
||||
|
||||
if( shader->num_stages )
|
||||
{
|
||||
|
@ -358,7 +353,7 @@ void R_DrawSky( ref_shader_t *shader )
|
|||
if( r_shownormals->integer )
|
||||
features |= MF_NORMALS;
|
||||
|
||||
for( i = 0; i < 5; i++ )
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] )
|
||||
continue;
|
||||
|
@ -434,11 +429,11 @@ DrawSkyPolygon
|
|||
*/
|
||||
void DrawSkyPolygon( int nump, vec3_t vecs )
|
||||
{
|
||||
int i, j;
|
||||
vec3_t v, av;
|
||||
float s, t, dv;
|
||||
int axis;
|
||||
float *vp;
|
||||
int i, j;
|
||||
vec3_t v, av;
|
||||
float s, t, dv;
|
||||
int axis;
|
||||
float *vp;
|
||||
|
||||
// decide which face it maps to
|
||||
VectorClear( v );
|
||||
|
@ -450,12 +445,11 @@ void DrawSkyPolygon( int nump, vec3_t vecs )
|
|||
av[1] = fabs( v[1] );
|
||||
av[2] = fabs( v[2] );
|
||||
|
||||
if( ( av[0] > av[1] ) && ( av[0] > av[2] ) )
|
||||
if(( av[0] > av[1] ) && ( av[0] > av[2] ))
|
||||
axis = ( v[0] < 0 ) ? 1 : 0;
|
||||
else if( ( av[1] > av[2] ) && ( av[1] > av[0] ) )
|
||||
else if(( av[1] > av[2] ) && ( av[1] > av[0] ))
|
||||
axis = ( v[1] < 0 ) ? 3 : 2;
|
||||
else
|
||||
axis = ( v[2] < 0 ) ? 5 : 4;
|
||||
else axis = ( v[2] < 0 ) ? 5 : 4;
|
||||
|
||||
if( !r_skyfog )
|
||||
r_skyfog = r_warpface->fog;
|
||||
|
@ -467,7 +461,7 @@ void DrawSkyPolygon( int nump, vec3_t vecs )
|
|||
j = vec_to_st[axis][2];
|
||||
dv = ( j > 0 ) ? vecs[j - 1] : -vecs[-j - 1];
|
||||
|
||||
if( dv < 0.001 )
|
||||
if( dv < 0.001f )
|
||||
continue; // don't divide by zero
|
||||
|
||||
dv = 1.0f / dv;
|
||||
|
@ -478,19 +472,13 @@ void DrawSkyPolygon( int nump, vec3_t vecs )
|
|||
j = vec_to_st[axis][1];
|
||||
t = ( j < 0 ) ? -vecs[-j -1] * dv : vecs[j-1] * dv;
|
||||
|
||||
if( s < RI.skyMins[0][axis] )
|
||||
RI.skyMins[0][axis] = s;
|
||||
if( t < RI.skyMins[1][axis] )
|
||||
RI.skyMins[1][axis] = t;
|
||||
if( s > RI.skyMaxs[0][axis] )
|
||||
RI.skyMaxs[0][axis] = s;
|
||||
if( t > RI.skyMaxs[1][axis] )
|
||||
RI.skyMaxs[1][axis] = t;
|
||||
if( s < RI.skyMins[0][axis] ) RI.skyMins[0][axis] = s;
|
||||
if( t < RI.skyMins[1][axis] ) RI.skyMins[1][axis] = t;
|
||||
if( s > RI.skyMaxs[0][axis] ) RI.skyMaxs[0][axis] = s;
|
||||
if( t > RI.skyMaxs[1][axis] ) RI.skyMaxs[1][axis] = t;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_CLIP_VERTS 64
|
||||
|
||||
/*
|
||||
==============
|
||||
ClipSkyPolygon
|
||||
|
@ -498,22 +486,23 @@ ClipSkyPolygon
|
|||
*/
|
||||
void ClipSkyPolygon( int nump, vec3_t vecs, int stage )
|
||||
{
|
||||
float *norm;
|
||||
float *v;
|
||||
bool front, back;
|
||||
float d, e;
|
||||
float dists[MAX_CLIP_VERTS + 1];
|
||||
int sides[MAX_CLIP_VERTS + 1];
|
||||
vec3_t newv[2][MAX_CLIP_VERTS + 1];
|
||||
int newc[2];
|
||||
int i, j;
|
||||
float *norm;
|
||||
float *v;
|
||||
bool front, back;
|
||||
float d, e;
|
||||
float dists[MAX_CLIP_VERTS + 1];
|
||||
int sides[MAX_CLIP_VERTS + 1];
|
||||
vec3_t newv[2][MAX_CLIP_VERTS + 1];
|
||||
int newc[2];
|
||||
int i, j;
|
||||
|
||||
if( nump > MAX_CLIP_VERTS )
|
||||
Host_Error( "ClipSkyPolygon: MAX_CLIP_VERTS\n" );
|
||||
|
||||
loc1:
|
||||
if( stage == 6 )
|
||||
{ // fully clipped, so draw it
|
||||
{
|
||||
// fully clipped, so draw it
|
||||
DrawSkyPolygon( nump, vecs );
|
||||
return;
|
||||
}
|
||||
|
@ -541,7 +530,8 @@ loc1:
|
|||
}
|
||||
|
||||
if( !front || !back )
|
||||
{ // not clipped
|
||||
{
|
||||
// not clipped
|
||||
stage++;
|
||||
goto loc1;
|
||||
}
|
||||
|
@ -598,16 +588,26 @@ R_AddSkySurface
|
|||
*/
|
||||
bool R_AddSkySurface( msurface_t *fa )
|
||||
{
|
||||
int i;
|
||||
vec4_t *vert;
|
||||
int i;
|
||||
vec4_t *vert;
|
||||
elem_t *elem;
|
||||
mesh_t *mesh;
|
||||
vec3_t verts[4];
|
||||
mesh_t *mesh;
|
||||
vec3_t verts[4];
|
||||
|
||||
// calculate vertex values for sky box
|
||||
r_warpface = fa;
|
||||
r_warpfacevis = false;
|
||||
|
||||
if( fa->shader->skySpeed )
|
||||
{
|
||||
// HACK: force full sky to draw when rotating
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
RI.skyMins[0][i] = RI.skyMins[1][i] = -1;
|
||||
RI.skyMaxs[0][i] = RI.skyMaxs[1][i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
mesh = fa->mesh;
|
||||
elem = mesh->elems;
|
||||
vert = mesh->xyzArray;
|
||||
|
@ -618,7 +618,6 @@ bool R_AddSkySurface( msurface_t *fa )
|
|||
VectorSubtract( vert[elem[2]], RI.viewOrigin, verts[2] );
|
||||
ClipSkyPolygon( 3, verts[0], 0 );
|
||||
}
|
||||
|
||||
return r_warpfacevis;
|
||||
}
|
||||
|
||||
|
@ -629,7 +628,7 @@ R_ClearSkyBox
|
|||
*/
|
||||
void R_ClearSkyBox( void )
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
|
||||
RI.params |= RP_NOSKY;
|
||||
for( i = 0; i < 6; i++ )
|
||||
|
@ -641,8 +640,8 @@ void R_ClearSkyBox( void )
|
|||
|
||||
void MakeSkyVec( float x, float y, float z, int axis, vec3_t v )
|
||||
{
|
||||
int j, k;
|
||||
vec3_t b;
|
||||
int j, k;
|
||||
vec3_t b;
|
||||
|
||||
b[0] = x;
|
||||
b[1] = y;
|
||||
|
@ -651,9 +650,7 @@ void MakeSkyVec( float x, float y, float z, int axis, vec3_t v )
|
|||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
k = st_to_vec[axis][j];
|
||||
if( k < 0 )
|
||||
v[j] = -b[-k - 1];
|
||||
else
|
||||
v[j] = b[k - 1];
|
||||
if( k < 0 ) v[j] = -b[-k-1];
|
||||
else v[j] = b[k-1];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,665 @@
|
|||
/*
|
||||
Copyright (C) 1999 Stephen C. Taylor
|
||||
Copyright (C) 2002-2007 Victor Luchits
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
// r_sky.c
|
||||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
|
||||
#define MAX_CLIP_VERTS 64
|
||||
#define SIDE_SIZE 9
|
||||
#define POINTS_LEN ( SIDE_SIZE * SIDE_SIZE )
|
||||
#define ELEM_LEN (( SIDE_SIZE-1 ) * ( SIDE_SIZE-1 ) * 6 )
|
||||
|
||||
#define SPHERE_RAD 10.0f
|
||||
#define EYE_RAD 9.0f
|
||||
|
||||
#define SCALE_S 4.0f // arbitrary (?) texture scaling factors
|
||||
#define SCALE_T 4.0f
|
||||
|
||||
#define BOX_SIZE 1.0f
|
||||
#define BOX_STEP BOX_SIZE / ( SIDE_SIZE-1 ) * 2.0f
|
||||
|
||||
elem_t r_skydome_elems[6][ELEM_LEN];
|
||||
meshbuffer_t r_skydome_mbuffer;
|
||||
|
||||
static mfog_t *r_skyfog;
|
||||
static msurface_t *r_warpface;
|
||||
static bool r_warpfacevis;
|
||||
|
||||
static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow, vec3_t dcol, float skyheight );
|
||||
static void MakeSkyVec( float x, float y, float z, int axis, vec3_t v );
|
||||
static void Gen_Box( skydome_t *skydome, float skyheight );
|
||||
|
||||
/*
|
||||
==============
|
||||
R_CreateSkydome
|
||||
==============
|
||||
*/
|
||||
skydome_t *R_CreateSkydome( byte *mempool, float skyheight, ref_shader_t **farboxShaders, ref_shader_t **nearboxShaders )
|
||||
{
|
||||
int i, size;
|
||||
mesh_t *mesh;
|
||||
skydome_t *skydome;
|
||||
byte *buffer;
|
||||
|
||||
size = sizeof( skydome_t ) + sizeof( mesh_t ) * 6 + sizeof( vec4_t ) * POINTS_LEN * 6 +
|
||||
sizeof( vec4_t ) * POINTS_LEN * 6 + sizeof( vec2_t ) * POINTS_LEN * 11;
|
||||
buffer = Mem_Alloc( mempool, size );
|
||||
|
||||
skydome = ( skydome_t * )buffer;
|
||||
Mem_Copy( skydome->farboxShaders, farboxShaders, sizeof( ref_shader_t* ) * 6 );
|
||||
Mem_Copy( skydome->nearboxShaders, nearboxShaders, sizeof( ref_shader_t* ) * 6 );
|
||||
buffer += sizeof( skydome_t );
|
||||
|
||||
skydome->meshes = ( mesh_t * )buffer;
|
||||
buffer += sizeof( mesh_t ) * 6;
|
||||
|
||||
for( i = 0, mesh = skydome->meshes; i < 6; i++, mesh++ )
|
||||
{
|
||||
mesh->numVertexes = POINTS_LEN;
|
||||
mesh->xyzArray = ( vec4_t * )buffer; buffer += sizeof( vec4_t ) * POINTS_LEN;
|
||||
mesh->normalsArray = ( vec4_t * )buffer; buffer += sizeof( vec4_t ) * POINTS_LEN;
|
||||
if( i != 5 )
|
||||
{
|
||||
skydome->sphereStCoords[i] = ( vec2_t * )buffer; buffer += sizeof( vec2_t ) * POINTS_LEN;
|
||||
}
|
||||
skydome->linearStCoords[i] = ( vec2_t * )buffer; buffer += sizeof( vec2_t ) * POINTS_LEN;
|
||||
|
||||
mesh->numElems = ELEM_LEN;
|
||||
mesh->elems = r_skydome_elems[i];
|
||||
}
|
||||
|
||||
Gen_Box( skydome, skyheight );
|
||||
|
||||
return skydome;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_FreeSkydome
|
||||
==============
|
||||
*/
|
||||
void R_FreeSkydome( skydome_t *skydome )
|
||||
{
|
||||
if( skydome ) Mem_Free( skydome );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
Gen_Box
|
||||
==============
|
||||
*/
|
||||
static void Gen_Box( skydome_t *skydome, float skyheight )
|
||||
{
|
||||
int axis;
|
||||
vec3_t orig, drow, dcol;
|
||||
|
||||
for( axis = 0; axis < 6; axis++ )
|
||||
{
|
||||
MakeSkyVec( -BOX_SIZE, -BOX_SIZE, BOX_SIZE, axis, orig );
|
||||
MakeSkyVec( 0, BOX_STEP, 0, axis, drow );
|
||||
MakeSkyVec( BOX_STEP, 0, 0, axis, dcol );
|
||||
Gen_BoxSide( skydome, axis, orig, drow, dcol, skyheight );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Gen_BoxSide
|
||||
|
||||
I don't know exactly what Q3A does for skybox texturing, but
|
||||
this is at least fairly close. We tile the texture onto the
|
||||
inside of a large sphere, and put the camera near the top of
|
||||
the sphere. We place the box around the camera, and cast rays
|
||||
through the box verts to the sphere to find the texture coordinates.
|
||||
================
|
||||
*/
|
||||
static void Gen_BoxSide( skydome_t *skydome, int side, vec3_t orig, vec3_t drow, vec3_t dcol, float skyheight )
|
||||
{
|
||||
vec3_t pos, w, row, norm;
|
||||
float *v, *n, *st = NULL, *st2;
|
||||
int r, c;
|
||||
float t, d, d2, b, b2, q[2], s;
|
||||
|
||||
s = 1.0 / ( SIDE_SIZE-1 );
|
||||
d = EYE_RAD; // sphere center to camera distance
|
||||
d2 = d * d;
|
||||
b = SPHERE_RAD; // sphere radius
|
||||
b2 = b * b;
|
||||
q[0] = 1.0 / ( 2.0 * SCALE_S );
|
||||
q[1] = 1.0 / ( 2.0 * SCALE_T );
|
||||
|
||||
v = skydome->meshes[side].xyzArray[0];
|
||||
n = skydome->meshes[side].normalsArray[0];
|
||||
if( side != 5 ) st = skydome->sphereStCoords[side][0];
|
||||
st2 = skydome->linearStCoords[side][0];
|
||||
|
||||
VectorCopy( orig, row );
|
||||
|
||||
// CrossProduct( dcol, drow, norm );
|
||||
// VectorNormalize( norm );
|
||||
VectorClear( norm );
|
||||
|
||||
for( r = 0; r < SIDE_SIZE; r++ )
|
||||
{
|
||||
VectorCopy( row, pos );
|
||||
for( c = 0; c < SIDE_SIZE; c++ )
|
||||
{
|
||||
// pos points from eye to vertex on box
|
||||
VectorScale( pos, skyheight, v );
|
||||
VectorCopy( pos, w );
|
||||
|
||||
// Normalize pos -> w
|
||||
VectorNormalize( w );
|
||||
|
||||
// Find distance along w to sphere
|
||||
t = sqrt( d2 * ( w[2] * w[2] - 1.0 ) + b2 ) - d * w[2];
|
||||
w[0] *= t;
|
||||
w[1] *= t;
|
||||
|
||||
if( st )
|
||||
{
|
||||
// use x and y on sphere as s and t
|
||||
// minus is here so skies scoll in correct (Q3A's) direction
|
||||
st[0] = -w[0] * q[0];
|
||||
st[1] = -w[1] * q[1];
|
||||
|
||||
// avoid bilerp seam
|
||||
st[0] = ( bound( -1, st[0], 1 ) + 1.0 ) * 0.5;
|
||||
st[1] = ( bound( -1, st[1], 1 ) + 1.0 ) * 0.5;
|
||||
}
|
||||
|
||||
st2[0] = c * s;
|
||||
st2[1] = 1.0 - r * s;
|
||||
|
||||
VectorAdd( pos, dcol, pos );
|
||||
VectorCopy( norm, n );
|
||||
|
||||
v += 4;
|
||||
n += 4;
|
||||
if( st ) st += 2;
|
||||
st2 += 2;
|
||||
}
|
||||
|
||||
VectorAdd( row, drow, row );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_DrawSkySide
|
||||
==============
|
||||
*/
|
||||
static void R_DrawSkySide( skydome_t *skydome, int side, ref_shader_t *shader, int features )
|
||||
{
|
||||
meshbuffer_t *mbuffer = &r_skydome_mbuffer;
|
||||
|
||||
if( RI.skyMins[0][side] >= RI.skyMaxs[0][side] || RI.skyMins[1][side] >= RI.skyMaxs[1][side] )
|
||||
return;
|
||||
|
||||
mbuffer->shaderkey = shader->sortkey;
|
||||
mbuffer->dlightbits = 0;
|
||||
mbuffer->sortkey = MB_FOG2NUM( r_skyfog );
|
||||
|
||||
skydome->meshes[side].stArray = skydome->linearStCoords[side];
|
||||
R_PushMesh( &skydome->meshes[side], features );
|
||||
R_RenderMeshBuffer( mbuffer );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_DrawSkyBox
|
||||
==============
|
||||
*/
|
||||
static void R_DrawSkyBox( skydome_t *skydome, ref_shader_t **shaders )
|
||||
{
|
||||
int i, features;
|
||||
const int skytexorder[6] = { SKYBOX_RIGHT, SKYBOX_FRONT, SKYBOX_LEFT, SKYBOX_BACK, SKYBOX_TOP, SKYBOX_BOTTOM };
|
||||
|
||||
features = shaders[0]->features;
|
||||
if( r_shownormals->integer )
|
||||
features |= MF_NORMALS;
|
||||
|
||||
for( i = 0; i < 6; i++ )
|
||||
R_DrawSkySide( skydome, i, shaders[skytexorder[i]], features );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_DrawBlackBottom
|
||||
|
||||
Draw dummy skybox side to prevent the HOM effect
|
||||
==============
|
||||
*/
|
||||
static void R_DrawBlackBottom( skydome_t *skydome )
|
||||
{
|
||||
int features;
|
||||
|
||||
features = tr.defaultShader->features;
|
||||
if( r_shownormals->integer )
|
||||
features |= MF_NORMALS;
|
||||
R_DrawSkySide( skydome, 5, tr.defaultShader, features );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_DrawSky
|
||||
==============
|
||||
*/
|
||||
void R_DrawSky( ref_shader_t *shader )
|
||||
{
|
||||
int i;
|
||||
vec3_t mins, maxs;
|
||||
mat4x4_t m, oldm;
|
||||
elem_t *elem;
|
||||
skydome_t *skydome;
|
||||
meshbuffer_t *mbuffer = &r_skydome_mbuffer;
|
||||
int u, v, umin, umax, vmin, vmax;
|
||||
|
||||
if( !shader ) return;
|
||||
skydome = shader->skyParms ? shader->skyParms : NULL;
|
||||
if( !skydome) return;
|
||||
|
||||
ClearBounds( mins, maxs );
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] )
|
||||
continue;
|
||||
|
||||
umin = (int)(( RI.skyMins[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 ));
|
||||
umax = (int)(( RI.skyMaxs[0][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 )) + 1;
|
||||
vmin = (int)(( RI.skyMins[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 ));
|
||||
vmax = (int)(( RI.skyMaxs[1][i] + 1.0f ) * 0.5f * (float)( SIDE_SIZE-1 )) + 1;
|
||||
|
||||
umin = bound( 0, umin, SIDE_SIZE-1 );
|
||||
umax = bound( 0, umax, SIDE_SIZE-1 );
|
||||
vmin = bound( 0, vmin, SIDE_SIZE-1 );
|
||||
vmax = bound( 0, vmax, SIDE_SIZE-1 );
|
||||
|
||||
// box elems in tristrip order
|
||||
elem = skydome->meshes[i].elems;
|
||||
for( v = vmin; v < vmax; v++ )
|
||||
{
|
||||
for( u = umin; u < umax; u++ )
|
||||
{
|
||||
elem[0] = v * SIDE_SIZE + u;
|
||||
elem[1] = elem[4] = elem[0] + SIDE_SIZE;
|
||||
elem[2] = elem[3] = elem[0] + 1;
|
||||
elem[5] = elem[1] + 1;
|
||||
elem += 6;
|
||||
}
|
||||
}
|
||||
|
||||
AddPointToBounds( skydome->meshes[i].xyzArray[vmin*SIDE_SIZE+umin], mins, maxs );
|
||||
AddPointToBounds( skydome->meshes[i].xyzArray[vmax*SIDE_SIZE+umax], mins, maxs );
|
||||
|
||||
skydome->meshes[i].numElems = ( vmax-vmin )*( umax-umin ) * 6;
|
||||
}
|
||||
|
||||
VectorAdd( mins, RI.viewOrigin, mins );
|
||||
VectorAdd( maxs, RI.viewOrigin, maxs );
|
||||
|
||||
if( RI.refdef.rdflags & RDF_SKYPORTALINVIEW )
|
||||
{
|
||||
R_DrawSkyPortal( &RI.refdef.skyportal, mins, maxs );
|
||||
return;
|
||||
}
|
||||
|
||||
// center skydome on camera to give the illusion of a larger space
|
||||
Matrix4_Copy( RI.modelviewMatrix, oldm );
|
||||
Matrix4_Copy( RI.worldviewMatrix, RI.modelviewMatrix );
|
||||
Matrix4_Copy( RI.worldviewMatrix, m );
|
||||
|
||||
if( shader->skySpeed )
|
||||
{
|
||||
float angle = shader->skySpeed * RI.refdef.time;
|
||||
Matrix4_Rotate( m, angle, shader->skyAxis[0], shader->skyAxis[1], shader->skyAxis[2] );
|
||||
}
|
||||
m[12] = m[13] = m[14] = 0.0f;
|
||||
m[15] = 1.0;
|
||||
pglLoadMatrixf( m );
|
||||
|
||||
gldepthmin = 1;
|
||||
gldepthmax = 1;
|
||||
pglDepthRange( gldepthmin, gldepthmax );
|
||||
|
||||
if( RI.params & RP_CLIPPLANE )
|
||||
pglDisable( GL_CLIP_PLANE0 );
|
||||
|
||||
// it can happen that sky surfaces have no fog hull specified
|
||||
// yet there's a global fog hull (see wvwq3dm7)
|
||||
if( !r_skyfog )
|
||||
r_skyfog = r_worldbrushmodel->globalfog;
|
||||
|
||||
if( skydome->farboxShaders[0] )
|
||||
R_DrawSkyBox( skydome, skydome->farboxShaders );
|
||||
else R_DrawBlackBottom( skydome );
|
||||
|
||||
if( shader->num_stages )
|
||||
{
|
||||
bool flush = false;
|
||||
int features = shader->features;
|
||||
|
||||
if( r_shownormals->integer )
|
||||
features |= MF_NORMALS;
|
||||
|
||||
for( i = 0; i < 5; i++ )
|
||||
{
|
||||
if( RI.skyMins[0][i] >= RI.skyMaxs[0][i] || RI.skyMins[1][i] >= RI.skyMaxs[1][i] )
|
||||
continue;
|
||||
|
||||
flush = true;
|
||||
mbuffer->shaderkey = shader->sortkey;
|
||||
mbuffer->dlightbits = 0;
|
||||
mbuffer->sortkey = MB_FOG2NUM( r_skyfog );
|
||||
|
||||
skydome->meshes[i].stArray = skydome->sphereStCoords[i];
|
||||
R_PushMesh( &skydome->meshes[i], features );
|
||||
}
|
||||
if( flush ) R_RenderMeshBuffer( mbuffer );
|
||||
}
|
||||
|
||||
if( skydome->nearboxShaders[0] )
|
||||
R_DrawSkyBox( skydome, skydome->nearboxShaders );
|
||||
|
||||
if( RI.params & RP_CLIPPLANE )
|
||||
pglEnable( GL_CLIP_PLANE0 );
|
||||
|
||||
Matrix4_Copy( oldm, RI.modelviewMatrix );
|
||||
pglLoadMatrixf( RI.worldviewMatrix );
|
||||
|
||||
gldepthmin = 0;
|
||||
gldepthmax = 1;
|
||||
pglDepthRange( gldepthmin, gldepthmax );
|
||||
|
||||
r_skyfog = NULL;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
vec3_t skyclip[6] = {
|
||||
{ 1, 1, 0 },
|
||||
{ 1, -1, 0 },
|
||||
{ 0, -1, 1 },
|
||||
{ 0, 1, 1 },
|
||||
{ 1, 0, 1 },
|
||||
{ -1, 0, 1 }
|
||||
};
|
||||
|
||||
// 1 = s, 2 = t, 3 = 2048
|
||||
int st_to_vec[6][3] =
|
||||
{
|
||||
{ 3, -1, 2 },
|
||||
{ -3, 1, 2 },
|
||||
|
||||
{ 1, 3, 2 },
|
||||
{ -1, -3, 2 },
|
||||
|
||||
{ -2, -1, 3 }, // 0 degrees yaw, look straight up
|
||||
{ 2, -1, -3 } // look straight down
|
||||
};
|
||||
|
||||
// s = [0]/[2], t = [1]/[2]
|
||||
int vec_to_st[6][3] =
|
||||
{
|
||||
{ -2, 3, 1 },
|
||||
{ 2, 3, -1 },
|
||||
|
||||
{ 1, 3, 2 },
|
||||
{ -1, 3, -2 },
|
||||
|
||||
{ -2, -1, 3 },
|
||||
{ -2, 1, -3 }
|
||||
};
|
||||
|
||||
/*
|
||||
==============
|
||||
DrawSkyPolygon
|
||||
==============
|
||||
*/
|
||||
void DrawSkyPolygon( int nump, vec3_t vecs )
|
||||
{
|
||||
int i, j;
|
||||
vec3_t v, av;
|
||||
float s, t, dv;
|
||||
int axis;
|
||||
float *vp;
|
||||
|
||||
// decide which face it maps to
|
||||
VectorClear( v );
|
||||
|
||||
for( i = 0, vp = vecs; i < nump; i++, vp += 3 )
|
||||
VectorAdd( vp, v, v );
|
||||
|
||||
av[0] = fabs( v[0] );
|
||||
av[1] = fabs( v[1] );
|
||||
av[2] = fabs( v[2] );
|
||||
|
||||
if(( av[0] > av[1] ) && ( av[0] > av[2] ))
|
||||
axis = ( v[0] < 0 ) ? 1 : 0;
|
||||
else if(( av[1] > av[2] ) && ( av[1] > av[0] ))
|
||||
axis = ( v[1] < 0 ) ? 3 : 2;
|
||||
else axis = ( v[2] < 0 ) ? 5 : 4;
|
||||
|
||||
if( !r_skyfog )
|
||||
r_skyfog = r_warpface->fog;
|
||||
r_warpfacevis = true;
|
||||
|
||||
// project new texture coords
|
||||
for( i = 0; i < nump; i++, vecs += 3 )
|
||||
{
|
||||
j = vec_to_st[axis][2];
|
||||
dv = ( j > 0 ) ? vecs[j - 1] : -vecs[-j - 1];
|
||||
|
||||
if( dv < 0.001f )
|
||||
continue; // don't divide by zero
|
||||
|
||||
dv = 1.0f / dv;
|
||||
|
||||
j = vec_to_st[axis][0];
|
||||
s = ( j < 0 ) ? -vecs[-j -1] * dv : vecs[j-1] * dv;
|
||||
|
||||
j = vec_to_st[axis][1];
|
||||
t = ( j < 0 ) ? -vecs[-j -1] * dv : vecs[j-1] * dv;
|
||||
|
||||
if( s < RI.skyMins[0][axis] ) RI.skyMins[0][axis] = s;
|
||||
if( t < RI.skyMins[1][axis] ) RI.skyMins[1][axis] = t;
|
||||
if( s > RI.skyMaxs[0][axis] ) RI.skyMaxs[0][axis] = s;
|
||||
if( t > RI.skyMaxs[1][axis] ) RI.skyMaxs[1][axis] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ClipSkyPolygon
|
||||
==============
|
||||
*/
|
||||
void ClipSkyPolygon( int nump, vec3_t vecs, int stage )
|
||||
{
|
||||
float *norm;
|
||||
float *v;
|
||||
bool front, back;
|
||||
float d, e;
|
||||
float dists[MAX_CLIP_VERTS + 1];
|
||||
int sides[MAX_CLIP_VERTS + 1];
|
||||
vec3_t newv[2][MAX_CLIP_VERTS + 1];
|
||||
int newc[2];
|
||||
int i, j;
|
||||
|
||||
if( nump > MAX_CLIP_VERTS )
|
||||
Host_Error( "ClipSkyPolygon: MAX_CLIP_VERTS\n" );
|
||||
|
||||
loc1:
|
||||
if( stage == 6 )
|
||||
{
|
||||
// fully clipped, so draw it
|
||||
DrawSkyPolygon( nump, vecs );
|
||||
return;
|
||||
}
|
||||
|
||||
front = back = false;
|
||||
norm = skyclip[stage];
|
||||
for( i = 0, v = vecs; i < nump; i++, v += 3 )
|
||||
{
|
||||
d = DotProduct( v, norm );
|
||||
if( d > ON_EPSILON )
|
||||
{
|
||||
front = true;
|
||||
sides[i] = SIDE_FRONT;
|
||||
}
|
||||
else if( d < -ON_EPSILON )
|
||||
{
|
||||
back = true;
|
||||
sides[i] = SIDE_BACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
sides[i] = SIDE_ON;
|
||||
}
|
||||
dists[i] = d;
|
||||
}
|
||||
|
||||
if( !front || !back )
|
||||
{
|
||||
// not clipped
|
||||
stage++;
|
||||
goto loc1;
|
||||
}
|
||||
|
||||
// clip it
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
VectorCopy( vecs, ( vecs+( i*3 ) ) );
|
||||
newc[0] = newc[1] = 0;
|
||||
|
||||
for( i = 0, v = vecs; i < nump; i++, v += 3 )
|
||||
{
|
||||
switch( sides[i] )
|
||||
{
|
||||
case SIDE_FRONT:
|
||||
VectorCopy( v, newv[0][newc[0]] );
|
||||
newc[0]++;
|
||||
break;
|
||||
case SIDE_BACK:
|
||||
VectorCopy( v, newv[1][newc[1]] );
|
||||
newc[1]++;
|
||||
break;
|
||||
case SIDE_ON:
|
||||
VectorCopy( v, newv[0][newc[0]] );
|
||||
newc[0]++;
|
||||
VectorCopy( v, newv[1][newc[1]] );
|
||||
newc[1]++;
|
||||
break;
|
||||
}
|
||||
|
||||
if( sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i] )
|
||||
continue;
|
||||
|
||||
d = dists[i] / ( dists[i] - dists[i+1] );
|
||||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
e = v[j] + d * ( v[j+3] - v[j] );
|
||||
newv[0][newc[0]][j] = e;
|
||||
newv[1][newc[1]][j] = e;
|
||||
}
|
||||
newc[0]++;
|
||||
newc[1]++;
|
||||
}
|
||||
|
||||
// continue
|
||||
ClipSkyPolygon( newc[0], newv[0][0], stage + 1 );
|
||||
ClipSkyPolygon( newc[1], newv[1][0], stage + 1 );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_AddSkySurface
|
||||
=================
|
||||
*/
|
||||
bool R_AddSkySurface( msurface_t *fa )
|
||||
{
|
||||
int i;
|
||||
vec4_t *vert;
|
||||
elem_t *elem;
|
||||
mesh_t *mesh;
|
||||
vec3_t verts[4];
|
||||
|
||||
// calculate vertex values for sky box
|
||||
r_warpface = fa;
|
||||
r_warpfacevis = false;
|
||||
|
||||
if( fa->shader->skySpeed )
|
||||
{
|
||||
// HACK: force full sky to draw when rotating
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
RI.skyMins[0][i] = RI.skyMins[1][i] = -1;
|
||||
RI.skyMaxs[0][i] = RI.skyMaxs[1][i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
mesh = fa->mesh;
|
||||
elem = mesh->elems;
|
||||
vert = mesh->xyzArray;
|
||||
for( i = 0; i < mesh->numElems; i += 3, elem += 3 )
|
||||
{
|
||||
VectorSubtract( vert[elem[0]], RI.viewOrigin, verts[0] );
|
||||
VectorSubtract( vert[elem[1]], RI.viewOrigin, verts[1] );
|
||||
VectorSubtract( vert[elem[2]], RI.viewOrigin, verts[2] );
|
||||
ClipSkyPolygon( 3, verts[0], 0 );
|
||||
}
|
||||
return r_warpfacevis;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
R_ClearSkyBox
|
||||
==============
|
||||
*/
|
||||
void R_ClearSkyBox( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
RI.params |= RP_NOSKY;
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
RI.skyMins[0][i] = RI.skyMins[1][i] = 9999999;
|
||||
RI.skyMaxs[0][i] = RI.skyMaxs[1][i] = -9999999;
|
||||
}
|
||||
}
|
||||
|
||||
void MakeSkyVec( float x, float y, float z, int axis, vec3_t v )
|
||||
{
|
||||
int j, k;
|
||||
vec3_t b;
|
||||
|
||||
b[0] = x;
|
||||
b[1] = y;
|
||||
b[2] = z;
|
||||
|
||||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
k = st_to_vec[axis][j];
|
||||
if( k < 0 ) v[j] = -b[-k-1];
|
||||
else v[j] = b[k-1];
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include "r_local.h"
|
||||
#include "mathlib.h"
|
||||
#include "quatlib.h"
|
||||
#include "matrix_lib.h"
|
||||
|
||||
static vec3_t modelorg; // relative to viewpoint
|
||||
static vec3_t modelmins;
|
||||
|
@ -78,7 +78,7 @@ bool R_CullSurface( msurface_t *surf, uint clipflags )
|
|||
|
||||
if( RI.currentmodel != r_worldmodel )
|
||||
{
|
||||
Matrix_TransformVector( RI.currententity->axis, surf->origin, origin );
|
||||
Matrix3x3_Transform( RI.currententity->axis, surf->origin, origin );
|
||||
VectorAdd( origin, RI.currententity->origin, origin );
|
||||
}
|
||||
else
|
||||
|
@ -138,7 +138,7 @@ static meshbuffer_t *R_AddSurfaceToList( msurface_t *surf, unsigned int clipflag
|
|||
if( shader->flags & SHADER_SKYPARMS )
|
||||
{
|
||||
bool vis = R_AddSkySurface( surf );
|
||||
if( ( RI.params & RP_NOSKY ) && vis )
|
||||
if(( RI.params & RP_NOSKY ) && vis )
|
||||
{
|
||||
R_AddMeshToList( MB_MODEL, surf->fog, shader, surf - r_worldbrushmodel->surfaces + 1 );
|
||||
RI.params &= ~RP_NOSKY;
|
||||
|
@ -176,7 +176,7 @@ bool R_CullBrushModel( ref_entity_t *e )
|
|||
if( bmodel->nummodelsurfaces == 0 )
|
||||
return true;
|
||||
|
||||
if( !Matrix_Compare( e->axis, axis_identity ) )
|
||||
if( !Matrix3x3_Compare( e->axis, matrix3x3_identity ))
|
||||
{
|
||||
rotated = true;
|
||||
for( i = 0; i < 3; i++ )
|
||||
|
@ -231,14 +231,14 @@ void R_AddBrushModelToList( ref_entity_t *e )
|
|||
e->outlineHeight = r_worldent->outlineHeight;
|
||||
Vector4Copy( r_worldent->outlineColor, e->outlineColor );
|
||||
|
||||
rotated = !Matrix_Compare( e->axis, axis_identity );
|
||||
rotated = !Matrix3x3_Compare( e->axis, matrix3x3_identity );
|
||||
VectorSubtract( RI.refdef.vieworg, e->origin, modelorg );
|
||||
if( rotated )
|
||||
{
|
||||
vec3_t temp;
|
||||
|
||||
VectorCopy( modelorg, temp );
|
||||
Matrix_TransformVector( e->axis, temp, modelorg );
|
||||
Matrix3x3_Transform( e->axis, temp, modelorg );
|
||||
}
|
||||
|
||||
dlightbits = 0;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<html>
|
||||
<body>
|
||||
<pre>
|
||||
<h1>Build Log</h1>
|
||||
<h3>
|
||||
--------------------Configuration: render - Win32 Debug--------------------
|
||||
</h3>
|
||||
<h3>Command Lines</h3>
|
||||
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7B9.tmp" with contents
|
||||
[
|
||||
/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\render\!debug/" /Fo"..\temp\render\!debug/" /Fd"..\temp\render\!debug/" /FD /c
|
||||
"D:\Xash3D\src_main\render\r_shader.c"
|
||||
]
|
||||
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7B9.tmp"
|
||||
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BA.tmp" with contents
|
||||
[
|
||||
msvcrtd.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"..\temp\render\!debug/render.pdb" /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"..\temp\render\!debug/render.dll" /implib:"..\temp\render\!debug/render.lib" /pdbtype:sept
|
||||
"\Xash3D\src_main\temp\render\!debug\cin.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_alias.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_backend.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_bloom.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_cin.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_cull.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_draw.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_image.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_light.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_main.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_math.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_mesh.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_model.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_opengl.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_poly.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_program.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_register.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_shader.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_shadow.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_skin.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_sky.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_sprite.obj"
|
||||
"\Xash3D\src_main\temp\render\!debug\r_surf.obj"
|
||||
]
|
||||
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BA.tmp"
|
||||
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BB.bat" with contents
|
||||
[
|
||||
@echo off
|
||||
copy \Xash3D\src_main\temp\render\!debug\render.dll "D:\Xash3D\bin\render.dll"
|
||||
]
|
||||
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP7BB.bat"
|
||||
Compiling...
|
||||
r_shader.c
|
||||
Linking...
|
||||
Creating library ..\temp\render\!debug/render.lib and object ..\temp\render\!debug/render.exp
|
||||
<h3>Output Window</h3>
|
||||
Performing Custom Build Step on \Xash3D\src_main\temp\render\!debug\render.dll
|
||||
‘ª®¯¨à®¢ ® ä ©«®¢: 1.
|
||||
|
||||
|
||||
|
||||
<h3>Results</h3>
|
||||
render.dll - 0 error(s), 0 warning(s)
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -34,6 +34,7 @@ public:
|
|||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( func_wall, CFuncWall );
|
||||
LINK_ENTITY_TO_CLASS( func_static, CFuncWall );
|
||||
LINK_ENTITY_TO_CLASS( func_wall_toggle, CFuncWall );
|
||||
LINK_ENTITY_TO_CLASS( func_illusionary, CFuncWall );
|
||||
|
||||
|
|
|
@ -182,7 +182,6 @@ void CPortalSurface :: Think( void )
|
|||
|
||||
void CPortalSurface :: PostActivate( void )
|
||||
{
|
||||
Vector dir;
|
||||
CBaseEntity *pTarget, *pOwner;
|
||||
|
||||
SetNextThink( 0 );
|
||||
|
|
|
@ -281,8 +281,8 @@ class CTriggerPush : public CBaseTrigger
|
|||
{
|
||||
void Spawn( void )
|
||||
{
|
||||
if ( pev->angles == g_vecZero ) pev->angles.y = 360;
|
||||
if (pev->speed == 0) pev->speed = 100;
|
||||
if( pev->angles == g_vecZero ) pev->angles.y = 360;
|
||||
if( pev->speed == 0 ) pev->speed = 100;
|
||||
UTIL_LinearVector( this );
|
||||
|
||||
if ( FBitSet (pev->spawnflags, 2) ) pev->solid = SOLID_NOT;
|
||||
|
@ -293,7 +293,25 @@ class CTriggerPush : public CBaseTrigger
|
|||
SetBits( pev->effects, EF_NODRAW );
|
||||
UTIL_SetOrigin( this, pev->origin );
|
||||
}
|
||||
void Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
void PostActivate( void )
|
||||
{
|
||||
Vector dir;
|
||||
CBaseEntity *pOwner;
|
||||
|
||||
if( FStringNull( pev->target ))
|
||||
return; // dir set with angles
|
||||
|
||||
pOwner = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ));
|
||||
if( !pOwner ) return; // dir set with angles
|
||||
|
||||
if( FClassnameIs( pOwner->pev, "target_position" ))
|
||||
{
|
||||
pev->owner = pOwner->edict();
|
||||
pev->movedir = pOwner->pev->origin - ((pev->absmin + pev->absmax) * 0.5f);
|
||||
pev->movedir.Normalize();
|
||||
}
|
||||
}
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
if( pev->solid == SOLID_NOT )
|
||||
{
|
||||
|
@ -314,13 +332,13 @@ class CTriggerPush : public CBaseTrigger
|
|||
return;
|
||||
}
|
||||
|
||||
if ( pOther->pev->solid != SOLID_NOT && pOther->pev->solid != SOLID_BSP )
|
||||
if( pOther->pev->solid != SOLID_NOT && pOther->pev->solid != SOLID_BSP )
|
||||
{
|
||||
// Instant trigger, just transfer velocity and remove
|
||||
if (FBitSet(pev->spawnflags, 1))
|
||||
// instant trigger, just transfer velocity and remove
|
||||
if( FBitSet( pev->spawnflags, 1 ))
|
||||
{
|
||||
pOther->pev->velocity = pOther->pev->velocity + (pev->speed * pev->movedir);
|
||||
if ( pOther->pev->velocity.z > 0 ) pOther->pev->flags &= ~FL_ONGROUND;
|
||||
if( pOther->pev->velocity.z > 0 ) pOther->pev->flags &= ~FL_ONGROUND;
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
else
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,132 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef ACTIVITY_H
|
||||
#define ACTIVITY_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity
|
||||
ACT_IDLE = 1,
|
||||
ACT_GUARD,
|
||||
ACT_WALK,
|
||||
ACT_RUN,
|
||||
ACT_FLY, // Fly (and flap if appropriate)
|
||||
ACT_SWIM,
|
||||
ACT_HOP, // vertical jump
|
||||
ACT_LEAP, // long forward jump
|
||||
ACT_FALL,
|
||||
ACT_LAND,
|
||||
ACT_STRAFE_LEFT,
|
||||
ACT_STRAFE_RIGHT,
|
||||
ACT_ROLL_LEFT, // tuck and roll, left
|
||||
ACT_ROLL_RIGHT, // tuck and roll, right
|
||||
ACT_TURN_LEFT, // turn quickly left (stationary)
|
||||
ACT_TURN_RIGHT, // turn quickly right (stationary)
|
||||
ACT_CROUCH, // the act of crouching down from a standing position
|
||||
ACT_CROUCHIDLE, // holding body in crouched position (loops)
|
||||
ACT_STAND, // the act of standing from a crouched position
|
||||
ACT_USE,
|
||||
ACT_SIGNAL1,
|
||||
ACT_SIGNAL2,
|
||||
ACT_SIGNAL3,
|
||||
ACT_TWITCH,
|
||||
ACT_COWER,
|
||||
ACT_SMALL_FLINCH,
|
||||
ACT_BIG_FLINCH,
|
||||
ACT_RANGE_ATTACK1,
|
||||
ACT_RANGE_ATTACK2,
|
||||
ACT_MELEE_ATTACK1,
|
||||
ACT_MELEE_ATTACK2,
|
||||
ACT_RELOAD,
|
||||
ACT_ARM, // pull out gun, for instance
|
||||
ACT_DISARM, // reholster gun
|
||||
ACT_EAT, // monster chowing on a large food item (loop)
|
||||
ACT_DIESIMPLE,
|
||||
ACT_DIEBACKWARD,
|
||||
ACT_DIEFORWARD,
|
||||
ACT_DIEVIOLENT,
|
||||
ACT_BARNACLE_HIT, // barnacle tongue hits a monster
|
||||
ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop )
|
||||
ACT_BARNACLE_CHOMP, // barnacle latches on to the monster
|
||||
ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop )
|
||||
ACT_SLEEP,
|
||||
ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor
|
||||
ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you
|
||||
ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop)
|
||||
ACT_WALK_HURT, // limp (loop)
|
||||
ACT_RUN_HURT, // limp (loop)
|
||||
ACT_HOVER, // Idle while in flight
|
||||
ACT_GLIDE, // Fly (don't flap)
|
||||
ACT_FLY_LEFT, // Turn left in flight
|
||||
ACT_FLY_RIGHT, // Turn right in flight
|
||||
ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air
|
||||
ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster
|
||||
ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops.
|
||||
ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc )
|
||||
ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of
|
||||
ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever
|
||||
ACT_SPECIAL_ATTACK1,// very monster specific special attacks.
|
||||
ACT_SPECIAL_ATTACK2,
|
||||
ACT_COMBAT_IDLE, // agitated idle.
|
||||
ACT_WALK_SCARED,
|
||||
ACT_RUN_SCARED,
|
||||
ACT_VICTORY_DANCE, // killed a player, do a victory dance.
|
||||
ACT_DIE_HEADSHOT, // die, hit in head.
|
||||
ACT_DIE_CHESTSHOT, // die, hit in chest
|
||||
ACT_DIE_GUTSHOT, // die, hit in gut
|
||||
ACT_DIE_BACKSHOT, // die, hit in back
|
||||
ACT_FLINCH_HEAD,
|
||||
ACT_FLINCH_CHEST,
|
||||
ACT_FLINCH_STOMACH,
|
||||
ACT_FLINCH_LEFTARM,
|
||||
ACT_FLINCH_RIGHTARM,
|
||||
ACT_FLINCH_LEFTLEG,
|
||||
ACT_FLINCH_RIGHTLEG,
|
||||
ACT_VM_NONE, // weapon viewmodel animations
|
||||
ACT_VM_DEPLOY, // deploy
|
||||
ACT_VM_DEPLOY_EMPTY,// deploy empty weapon
|
||||
ACT_VM_HOLSTER, // holster empty weapon
|
||||
ACT_VM_HOLSTER_EMPTY,
|
||||
ACT_VM_IDLE1,
|
||||
ACT_VM_IDLE2,
|
||||
ACT_VM_IDLE3,
|
||||
ACT_VM_RANGE_ATTACK1,
|
||||
ACT_VM_RANGE_ATTACK2,
|
||||
ACT_VM_RANGE_ATTACK3,
|
||||
ACT_VM_MELEE_ATTACK1,
|
||||
ACT_VM_MELEE_ATTACK2,
|
||||
ACT_VM_MELEE_ATTACK3,
|
||||
ACT_VM_SHOOT_EMPTY,
|
||||
ACT_VM_START_RELOAD,
|
||||
ACT_VM_RELOAD,
|
||||
ACT_VM_RELOAD_EMPTY,
|
||||
ACT_VM_TURNON,
|
||||
ACT_VM_TURNOFF,
|
||||
ACT_VM_PUMP, // pumping gun
|
||||
ACT_VM_PUMP_EMPTY,
|
||||
ACT_VM_START_CHARGE,
|
||||
ACT_VM_CHARGE,
|
||||
ACT_VM_OVERLOAD,
|
||||
ACT_VM_IDLE_EMPTY,
|
||||
} Activity;
|
||||
|
||||
// studio activity map conversion
|
||||
typedef struct { int type; char *name; } activity_map_t;
|
||||
|
||||
extern activity_map_t activity_map[];
|
||||
|
||||
|
||||
#endif//ACTIVITY_H
|
|
@ -0,0 +1,121 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
|
||||
activity_map_t activity_map[] =
|
||||
{
|
||||
{ACT_IDLE, "ACT_IDLE" },
|
||||
{ACT_GUARD, "ACT_GUARD" },
|
||||
{ACT_WALK, "ACT_WALK" },
|
||||
{ACT_RUN, "ACT_RUN" },
|
||||
{ACT_FLY, "ACT_FLY" },
|
||||
{ACT_SWIM, "ACT_SWIM", },
|
||||
{ACT_HOP, "ACT_HOP", },
|
||||
{ACT_LEAP, "ACT_LEAP" },
|
||||
{ACT_FALL, "ACT_FALL" },
|
||||
{ACT_LAND, "ACT_LAND" },
|
||||
{ACT_STRAFE_LEFT, "ACT_STRAFE_LEFT" },
|
||||
{ACT_STRAFE_RIGHT, "ACT_STRAFE_RIGHT" },
|
||||
{ACT_ROLL_LEFT, "ACT_ROLL_LEFT" },
|
||||
{ACT_ROLL_RIGHT, "ACT_ROLL_RIGHT" },
|
||||
{ACT_TURN_LEFT, "ACT_TURN_LEFT" },
|
||||
{ACT_TURN_RIGHT, "ACT_TURN_RIGHT" },
|
||||
{ACT_CROUCH, "ACT_CROUCH" },
|
||||
{ACT_CROUCHIDLE, "ACT_CROUCHIDLE" },
|
||||
{ACT_STAND, "ACT_STAND" },
|
||||
{ACT_USE, "ACT_USE" },
|
||||
{ACT_SIGNAL1, "ACT_SIGNAL1" },
|
||||
{ACT_SIGNAL2, "ACT_SIGNAL2" },
|
||||
{ACT_SIGNAL3, "ACT_SIGNAL3" },
|
||||
{ACT_TWITCH, "ACT_TWITCH" },
|
||||
{ACT_COWER, "ACT_COWER" },
|
||||
{ACT_SMALL_FLINCH, "ACT_SMALL_FLINCH" },
|
||||
{ACT_BIG_FLINCH, "ACT_BIG_FLINCH" },
|
||||
{ACT_RANGE_ATTACK1, "ACT_RANGE_ATTACK1" },
|
||||
{ACT_RANGE_ATTACK2, "ACT_RANGE_ATTACK2" },
|
||||
{ACT_MELEE_ATTACK1, "ACT_MELEE_ATTACK1" },
|
||||
{ACT_MELEE_ATTACK2, "ACT_MELEE_ATTACK2" },
|
||||
{ACT_RELOAD, "ACT_RELOAD" },
|
||||
{ACT_ARM, "ACT_ARM" },
|
||||
{ACT_DISARM, "ACT_DISARM" },
|
||||
{ACT_EAT, "ACT_EAT" },
|
||||
{ACT_DIESIMPLE, "ACT_DIESIMPLE" },
|
||||
{ACT_DIEBACKWARD, "ACT_DIEBACKWARD" },
|
||||
{ACT_DIEFORWARD, "ACT_DIEFORWARD" },
|
||||
{ACT_DIEVIOLENT, "ACT_DIEVIOLENT" },
|
||||
{ACT_BARNACLE_HIT, "ACT_BARNACLE_HIT" },
|
||||
{ACT_BARNACLE_PULL, "ACT_BARNACLE_PULL" },
|
||||
{ACT_BARNACLE_CHOMP, "ACT_BARNACLE_CHOMP" },
|
||||
{ACT_BARNACLE_CHEW, "ACT_BARNACLE_CHEW" },
|
||||
{ACT_SLEEP, "ACT_SLEEP" },
|
||||
{ACT_INSPECT_FLOOR, "ACT_INSPECT_FLOOR" },
|
||||
{ACT_INSPECT_WALL, "ACT_INSPECT_WALL" },
|
||||
{ACT_IDLE_ANGRY, "ACT_IDLE_ANGRY" },
|
||||
{ACT_WALK_HURT, "ACT_WALK_HURT" },
|
||||
{ACT_RUN_HURT, "ACT_RUN_HURT" },
|
||||
{ACT_HOVER, "ACT_HOVER" },
|
||||
{ACT_GLIDE, "ACT_GLIDE" },
|
||||
{ACT_FLY_LEFT, "ACT_FLY_LEFT" },
|
||||
{ACT_FLY_RIGHT, "ACT_FLY_RIGHT" },
|
||||
{ACT_DETECT_SCENT, "ACT_DETECT_SCENT" },
|
||||
{ACT_SNIFF, "ACT_SNIFF" },
|
||||
{ACT_BITE, "ACT_BITE" },
|
||||
{ACT_THREAT_DISPLAY, "ACT_THREAT_DISPLAY" },
|
||||
{ACT_FEAR_DISPLAY, "ACT_FEAR_DISPLAY" },
|
||||
{ACT_EXCITED, "ACT_EXCITED" },
|
||||
{ACT_SPECIAL_ATTACK1, "ACT_SPECIAL_ATTACK1" },
|
||||
{ACT_SPECIAL_ATTACK2, "ACT_SPECIAL_ATTACK2" },
|
||||
{ACT_COMBAT_IDLE, "ACT_COMBAT_IDLE" },
|
||||
{ACT_WALK_SCARED, "ACT_WALK_SCARED" },
|
||||
{ACT_RUN_SCARED, "ACT_RUN_SCARED" },
|
||||
{ACT_VICTORY_DANCE, "ACT_VICTORY_DANCE" },
|
||||
{ACT_DIE_HEADSHOT, "ACT_DIE_HEADSHOT" },
|
||||
{ACT_DIE_CHESTSHOT, "ACT_DIE_CHESTSHOT" },
|
||||
{ACT_DIE_GUTSHOT, "ACT_DIE_GUTSHOT" },
|
||||
{ACT_DIE_BACKSHOT, "ACT_DIE_BACKSHOT" },
|
||||
{ACT_FLINCH_HEAD, "ACT_FLINCH_HEAD" },
|
||||
{ACT_FLINCH_CHEST, "ACT_FLINCH_CHEST" },
|
||||
{ACT_FLINCH_STOMACH, "ACT_FLINCH_STOMACH" },
|
||||
{ACT_FLINCH_LEFTARM, "ACT_FLINCH_LEFTARM" },
|
||||
{ACT_FLINCH_RIGHTARM, "ACT_FLINCH_RIGHTARM" },
|
||||
{ACT_FLINCH_LEFTLEG, "ACT_FLINCH_LEFTLEG" },
|
||||
{ACT_FLINCH_RIGHTLEG, "ACT_FLINCH_RIGHTLEG" },
|
||||
{ACT_VM_NONE, "ACT_VM_NONE" }, // invalid animation
|
||||
{ACT_VM_DEPLOY, "ACT_VM_DEPLOY" }, // deploy
|
||||
{ACT_VM_DEPLOY_EMPTY, "ACT_VM_DEPLOY_EMPTY" }, // deploy empty weapon
|
||||
{ACT_VM_HOLSTER, "ACT_VM_HOLSTER" }, // holster empty weapon
|
||||
{ACT_VM_HOLSTER_EMPTY, "ACT_VM_HOLSTER_EMPTY" },
|
||||
{ACT_VM_IDLE1, "ACT_VM_IDLE1" },
|
||||
{ACT_VM_IDLE2, "ACT_VM_IDLE2" },
|
||||
{ACT_VM_IDLE3, "ACT_VM_IDLE3" },
|
||||
{ACT_VM_RANGE_ATTACK1, "ACT_VM_RANGE_ATTACK1" },
|
||||
{ACT_VM_RANGE_ATTACK2, "ACT_VM_RANGE_ATTACK2" },
|
||||
{ACT_VM_RANGE_ATTACK3, "ACT_VM_RANGE_ATTACK3" },
|
||||
{ACT_VM_MELEE_ATTACK1, "ACT_VM_MELEE_ATTACK1" },
|
||||
{ACT_VM_MELEE_ATTACK2, "ACT_VM_MELEE_ATTACK2" },
|
||||
{ACT_VM_MELEE_ATTACK3, "ACT_VM_MELEE_ATTACK3" },
|
||||
{ACT_VM_SHOOT_EMPTY, "ACT_VM_SHOOT_EMPTY" },
|
||||
{ACT_VM_START_RELOAD, "ACT_VM_START_RELOAD" },
|
||||
{ACT_VM_RELOAD, "ACT_VM_RELOAD" },
|
||||
{ACT_VM_RELOAD_EMPTY, "ACT_VM_RELOAD_EMPTY" },
|
||||
{ACT_VM_TURNON, "ACT_VM_TURNON" },
|
||||
{ACT_VM_TURNOFF, "ACT_VM_TURNOFF" },
|
||||
{ACT_VM_PUMP, "ACT_VM_PUMP" }, // user animations
|
||||
{ACT_VM_PUMP_EMPTY, "ACT_VM_PUMP_EMPTY" },
|
||||
{ACT_VM_START_CHARGE, "ACT_VM_START_CHARGE" },
|
||||
{ACT_VM_CHARGE, "ACT_VM_CHARGE" },
|
||||
{ACT_VM_OVERLOAD, "ACT_VM_OVERLOAD" },
|
||||
{ACT_VM_IDLE_EMPTY, "ACT_VM_IDLE_EMPTY" },
|
||||
{0, NULL },
|
||||
};
|
|
@ -0,0 +1,379 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "soundent.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( soundent, CSoundEnt );
|
||||
|
||||
CSoundEnt *pSoundEnt;
|
||||
|
||||
//=========================================================
|
||||
// CSound - Clear - zeros all fields for a sound
|
||||
//=========================================================
|
||||
void CSound :: Clear ( void )
|
||||
{
|
||||
m_vecOrigin = g_vecZero;
|
||||
m_iType = 0;
|
||||
m_iVolume = 0;
|
||||
m_flExpireTime = 0;
|
||||
m_iNext = SOUNDLIST_EMPTY;
|
||||
m_iNextAudible = 0;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Reset - clears the volume, origin, and type for a sound,
|
||||
// but doesn't expire or unlink it.
|
||||
//=========================================================
|
||||
void CSound :: Reset ( void )
|
||||
{
|
||||
m_vecOrigin = g_vecZero;
|
||||
m_iType = 0;
|
||||
m_iVolume = 0;
|
||||
m_iNext = SOUNDLIST_EMPTY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FIsSound - returns TRUE if the sound is an Audible sound
|
||||
//=========================================================
|
||||
BOOL CSound :: FIsSound ( void )
|
||||
{
|
||||
if ( m_iType & ( bits_SOUND_COMBAT | bits_SOUND_WORLD | bits_SOUND_PLAYER | bits_SOUND_DANGER ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FIsScent - returns TRUE if the sound is actually a scent
|
||||
//=========================================================
|
||||
BOOL CSound :: FIsScent ( void )
|
||||
{
|
||||
if ( m_iType & ( bits_SOUND_CARCASS | bits_SOUND_MEAT | bits_SOUND_GARBAGE ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CSoundEnt :: Spawn( void )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
Initialize();
|
||||
|
||||
SetNextThink( 1 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Think - at interval, the entire active sound list is checked
|
||||
// for sounds that have ExpireTimes less than or equal
|
||||
// to the current world time, and these sounds are deallocated.
|
||||
//=========================================================
|
||||
void CSoundEnt :: Think ( void )
|
||||
{
|
||||
int iSound;
|
||||
int iPreviousSound;
|
||||
|
||||
SetNextThink( 0.3 );// how often to check the sound list.
|
||||
|
||||
iPreviousSound = SOUNDLIST_EMPTY;
|
||||
iSound = m_iActiveSound;
|
||||
|
||||
while ( iSound != SOUNDLIST_EMPTY )
|
||||
{
|
||||
if ( m_SoundPool[ iSound ].m_flExpireTime <= gpGlobals->time && m_SoundPool[ iSound ].m_flExpireTime != SOUND_NEVER_EXPIRE )
|
||||
{
|
||||
int iNext = m_SoundPool[ iSound ].m_iNext;
|
||||
|
||||
// move this sound back into the free list
|
||||
FreeSound( iSound, iPreviousSound );
|
||||
|
||||
iSound = iNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
iPreviousSound = iSound;
|
||||
iSound = m_SoundPool[ iSound ].m_iNext;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_fShowReport )
|
||||
{
|
||||
ALERT ( at_aiconsole, "Soundlist: %d / %d (%d)\n", ISoundsInList( SOUNDLISTTYPE_ACTIVE ),ISoundsInList( SOUNDLISTTYPE_FREE ), ISoundsInList( SOUNDLISTTYPE_ACTIVE ) - m_cLastActiveSounds );
|
||||
m_cLastActiveSounds = ISoundsInList ( SOUNDLISTTYPE_ACTIVE );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - dummy function
|
||||
//=========================================================
|
||||
void CSoundEnt :: Precache ( void )
|
||||
{
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FreeSound - clears the passed active sound and moves it
|
||||
// to the top of the free list. TAKE CARE to only call this
|
||||
// function for sounds in the Active list!!
|
||||
//=========================================================
|
||||
void CSoundEnt :: FreeSound ( int iSound, int iPrevious )
|
||||
{
|
||||
if ( !pSoundEnt )
|
||||
{
|
||||
// no sound ent!
|
||||
return;
|
||||
}
|
||||
|
||||
if ( iPrevious != SOUNDLIST_EMPTY )
|
||||
{
|
||||
// iSound is not the head of the active list, so
|
||||
// must fix the index for the Previous sound
|
||||
// pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = m_SoundPool[ iSound ].m_iNext;
|
||||
pSoundEnt->m_SoundPool[ iPrevious ].m_iNext = pSoundEnt->m_SoundPool[ iSound ].m_iNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the sound we're freeing IS the head of the active list.
|
||||
pSoundEnt->m_iActiveSound = pSoundEnt->m_SoundPool [ iSound ].m_iNext;
|
||||
}
|
||||
|
||||
// make iSound the head of the Free list.
|
||||
pSoundEnt->m_SoundPool[ iSound ].m_iNext = pSoundEnt->m_iFreeSound;
|
||||
pSoundEnt->m_iFreeSound = iSound;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IAllocSound - moves a sound from the Free list to the
|
||||
// Active list returns the index of the alloc'd sound
|
||||
//=========================================================
|
||||
int CSoundEnt :: IAllocSound( void )
|
||||
{
|
||||
int iNewSound;
|
||||
|
||||
if ( m_iFreeSound == SOUNDLIST_EMPTY )
|
||||
{
|
||||
// no free sound!
|
||||
ALERT ( at_console, "Free Sound List is full!\n" );
|
||||
return SOUNDLIST_EMPTY;
|
||||
}
|
||||
|
||||
// there is at least one sound available, so move it to the
|
||||
// Active sound list, and return its SoundPool index.
|
||||
|
||||
iNewSound = m_iFreeSound;// copy the index of the next free sound
|
||||
|
||||
m_iFreeSound = m_SoundPool[ m_iFreeSound ].m_iNext;// move the index down into the free list.
|
||||
|
||||
m_SoundPool[ iNewSound ].m_iNext = m_iActiveSound;// point the new sound at the top of the active list.
|
||||
|
||||
m_iActiveSound = iNewSound;// now make the new sound the top of the active list. You're done.
|
||||
|
||||
return iNewSound;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// InsertSound - Allocates a free sound and fills it with
|
||||
// sound info.
|
||||
//=========================================================
|
||||
void CSoundEnt :: InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration )
|
||||
{
|
||||
int iThisSound;
|
||||
|
||||
if ( !pSoundEnt )
|
||||
{
|
||||
// no sound ent!
|
||||
return;
|
||||
}
|
||||
|
||||
iThisSound = pSoundEnt->IAllocSound();
|
||||
|
||||
if ( iThisSound == SOUNDLIST_EMPTY )
|
||||
{
|
||||
ALERT( at_console, "Could not AllocSound() for InsertSound() (DLL)\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
pSoundEnt->m_SoundPool[ iThisSound ].m_vecOrigin = vecOrigin;
|
||||
pSoundEnt->m_SoundPool[ iThisSound ].m_iType = iType;
|
||||
pSoundEnt->m_SoundPool[ iThisSound ].m_iVolume = iVolume;
|
||||
pSoundEnt->m_SoundPool[ iThisSound ].m_flExpireTime = gpGlobals->time + flDuration;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Initialize - clears all sounds and moves them into the
|
||||
// free sound list.
|
||||
//=========================================================
|
||||
void CSoundEnt :: Initialize ( void )
|
||||
{
|
||||
int i;
|
||||
int iSound;
|
||||
|
||||
m_cLastActiveSounds;
|
||||
m_iFreeSound = 0;
|
||||
m_iActiveSound = SOUNDLIST_EMPTY;
|
||||
|
||||
for ( i = 0 ; i < MAX_WORLD_SOUNDS ; i++ )
|
||||
{// clear all sounds, and link them into the free sound list.
|
||||
m_SoundPool[ i ].Clear();
|
||||
m_SoundPool[ i ].m_iNext = i + 1;
|
||||
}
|
||||
|
||||
m_SoundPool[ i - 1 ].m_iNext = SOUNDLIST_EMPTY;// terminate the list here.
|
||||
|
||||
|
||||
// now reserve enough sounds for each client
|
||||
for ( i = 0 ; i < gpGlobals->maxClients ; i++ )
|
||||
{
|
||||
iSound = pSoundEnt->IAllocSound();
|
||||
|
||||
if ( iSound == SOUNDLIST_EMPTY )
|
||||
{
|
||||
ALERT ( at_console, "Could not AllocSound() for Client Reserve! (DLL)\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
pSoundEnt->m_SoundPool[ iSound ].m_flExpireTime = SOUND_NEVER_EXPIRE;
|
||||
}
|
||||
|
||||
if ( CVAR_GET_FLOAT("displaysoundlist") == 1 )
|
||||
{
|
||||
m_fShowReport = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fShowReport = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ISoundsInList - returns the number of sounds in the desired
|
||||
// sound list.
|
||||
//=========================================================
|
||||
int CSoundEnt :: ISoundsInList ( int iListType )
|
||||
{
|
||||
int i;
|
||||
int iThisSound;
|
||||
|
||||
if ( iListType == SOUNDLISTTYPE_FREE )
|
||||
{
|
||||
iThisSound = m_iFreeSound;
|
||||
}
|
||||
else if ( iListType == SOUNDLISTTYPE_ACTIVE )
|
||||
{
|
||||
iThisSound = m_iActiveSound;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT( at_console, "Unknown Sound List Type!\n" );
|
||||
}
|
||||
|
||||
if ( iThisSound == SOUNDLIST_EMPTY )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while ( iThisSound != SOUNDLIST_EMPTY )
|
||||
{
|
||||
i++;
|
||||
|
||||
iThisSound = m_SoundPool[ iThisSound ].m_iNext;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ActiveList - returns the head of the active sound list
|
||||
//=========================================================
|
||||
int CSoundEnt :: ActiveList ( void )
|
||||
{
|
||||
if ( !pSoundEnt )
|
||||
{
|
||||
return SOUNDLIST_EMPTY;
|
||||
}
|
||||
|
||||
return pSoundEnt->m_iActiveSound;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FreeList - returns the head of the free sound list
|
||||
//=========================================================
|
||||
int CSoundEnt :: FreeList ( void )
|
||||
{
|
||||
if ( !pSoundEnt )
|
||||
{
|
||||
return SOUNDLIST_EMPTY;
|
||||
}
|
||||
|
||||
return pSoundEnt->m_iFreeSound;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SoundPointerForIndex - returns a pointer to the instance
|
||||
// of CSound at index's position in the sound pool.
|
||||
//=========================================================
|
||||
CSound* CSoundEnt :: SoundPointerForIndex( int iIndex )
|
||||
{
|
||||
if ( !pSoundEnt )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( iIndex > ( MAX_WORLD_SOUNDS - 1 ))
|
||||
{
|
||||
ALERT( at_console, "SoundPointerForIndex() - Index too large!\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( iIndex < 0 )
|
||||
{
|
||||
ALERT( at_console, "SoundPointerForIndex() - Index < 0!\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &pSoundEnt->m_SoundPool[ iIndex ];
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Clients are numbered from 1 to MAXCLIENTS, but the client
|
||||
// reserved sounds in the soundlist are from 0 to MAXCLIENTS - 1,
|
||||
// so this function ensures that a client gets the proper index
|
||||
// to his reserved sound in the soundlist.
|
||||
//=========================================================
|
||||
int CSoundEnt :: ClientSoundIndex ( edict_t *pClient )
|
||||
{
|
||||
int iReturn = ENTINDEX( pClient ) - 1;
|
||||
|
||||
#ifdef _DEBUG
|
||||
if ( iReturn < 0 || iReturn > gpGlobals->maxClients )
|
||||
{
|
||||
ALERT( at_console, "** ClientSoundIndex returning a bogus value! **\n" );
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
return iReturn;
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== monsters.cpp ========================================================
|
||||
|
||||
Monster-related utility code
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "animation.h"
|
||||
#include "saverestore.h"
|
||||
|
||||
TYPEDESCRIPTION CBaseAnimating::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBaseMonster, m_flFrameRate, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBaseMonster, m_flGroundSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBaseMonster, m_flLastEventCheck, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBaseMonster, m_fSequenceFinished, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBaseMonster, m_fSequenceLoops, FIELD_BOOLEAN ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBaseAnimating, CBaseLogic );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// StudioFrameAdvance - advance the animation frame up to the current time
|
||||
// if an flInterval is passed in, only advance animation that number of seconds
|
||||
//=========================================================
|
||||
float CBaseAnimating :: StudioFrameAdvance ( float flInterval )
|
||||
{
|
||||
if (flInterval == 0.0)
|
||||
{
|
||||
flInterval = (gpGlobals->time - pev->animtime);
|
||||
if (flInterval <= 0.001)
|
||||
{
|
||||
pev->animtime = gpGlobals->time;
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
if (! pev->animtime)
|
||||
flInterval = 0.0;
|
||||
|
||||
pev->frame += flInterval * m_flFrameRate * pev->framerate;
|
||||
pev->animtime = gpGlobals->time;
|
||||
|
||||
if (pev->frame < 0.0 || pev->frame >= 256.0)
|
||||
{
|
||||
if (m_fSequenceLoops)
|
||||
pev->frame -= (int)(pev->frame / 256.0) * 256.0;
|
||||
else
|
||||
pev->frame = (pev->frame < 0.0) ? 0 : 255;
|
||||
m_fSequenceFinished = TRUE; // just in case it wasn't caught in GetEvents
|
||||
}
|
||||
|
||||
return flInterval;
|
||||
}
|
||||
|
||||
float CBaseAnimating :: SequenceDuration( int iSequence )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
void *pmodel = GET_MODEL_PTR(ENT(pev));
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
|
||||
if (!pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + iSequence;
|
||||
return (float)pseqdesc->numframes/(float)pseqdesc->fps;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LookupActivity
|
||||
//=========================================================
|
||||
int CBaseAnimating :: LookupActivity ( int activity )
|
||||
{
|
||||
ASSERT( activity != 0 );
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::LookupActivity( pmodel, pev, activity );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LookupActivityHeaviest
|
||||
//
|
||||
// Get activity with highest 'weight'
|
||||
//
|
||||
//=========================================================
|
||||
int CBaseAnimating :: LookupActivityHeaviest ( int activity )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::LookupActivityHeaviest( pmodel, pev, activity );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CBaseAnimating :: LookupSequence ( const char *label )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::LookupSequence( pmodel, label );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: ResetSequenceInfo ( )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
GetSequenceInfo( pmodel, pev, &m_flFrameRate, &m_flGroundSpeed );
|
||||
m_fSequenceLoops = ((GetSequenceFlags() & STUDIO_LOOPING) != 0);
|
||||
pev->animtime = gpGlobals->time;
|
||||
pev->framerate = 1.0;
|
||||
m_fSequenceFinished = FALSE;
|
||||
m_flLastEventCheck = gpGlobals->time;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
BOOL CBaseAnimating :: GetSequenceFlags( )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::GetSequenceFlags( pmodel, pev );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DispatchAnimEvents
|
||||
//=========================================================
|
||||
void CBaseAnimating :: DispatchAnimEvents ( float flInterval )
|
||||
{
|
||||
MonsterEvent_t event;
|
||||
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
if ( !pmodel )
|
||||
{
|
||||
ALERT( at_aiconsole, "Gibbed monster is thinking!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: I have to do this or some events get missed, and this is probably causing the problem below
|
||||
flInterval = 0.1;
|
||||
|
||||
// FIX: this still sometimes hits events twice
|
||||
float flStart = pev->frame + (m_flLastEventCheck - pev->animtime) * m_flFrameRate * pev->framerate;
|
||||
float flEnd = pev->frame + flInterval * m_flFrameRate * pev->framerate;
|
||||
m_flLastEventCheck = pev->animtime + flInterval;
|
||||
|
||||
m_fSequenceFinished = FALSE;
|
||||
if (flEnd >= 256 || flEnd <= 0.0)
|
||||
m_fSequenceFinished = TRUE;
|
||||
|
||||
int index = 0;
|
||||
|
||||
while ( (index = GetAnimationEvent( pmodel, pev, &event, flStart, flEnd, index ) ) != 0 )
|
||||
{
|
||||
HandleAnimEvent( &event );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
float CBaseAnimating :: SetBoneController ( int iController, float flValue )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return SetController( pmodel, pev, iController, flValue );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: InitBoneControllers ( void )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
SetController( pmodel, pev, 0, 0.0 );
|
||||
SetController( pmodel, pev, 1, 0.0 );
|
||||
SetController( pmodel, pev, 2, 0.0 );
|
||||
SetController( pmodel, pev, 3, 0.0 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
float CBaseAnimating :: SetBlending ( int iBlender, float flValue )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
return ::SetBlending( pmodel, pev, iBlender, flValue );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: GetBonePosition ( int iBone, Vector &origin, Vector &angles )
|
||||
{
|
||||
GET_BONE_POSITION( ENT(pev), iBone, origin, angles );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
BOOL CBaseAnimating :: GetAttachment ( int iAttachment, Vector &origin, Vector &angles )
|
||||
{
|
||||
GET_ATTACHMENT( ENT(pev), iAttachment, origin, angles );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
int CBaseAnimating :: FindTransition( int iEndingSequence, int iGoalSequence, int *piDir )
|
||||
{
|
||||
void *pmodel = GET_MODEL_PTR( ENT(pev) );
|
||||
|
||||
if (piDir == NULL)
|
||||
{
|
||||
int iDir;
|
||||
int sequence = ::FindTransition( pmodel, iEndingSequence, iGoalSequence, &iDir );
|
||||
if (iDir != 1)
|
||||
return -1;
|
||||
else
|
||||
return sequence;
|
||||
}
|
||||
|
||||
return ::FindTransition( pmodel, iEndingSequence, iGoalSequence, piDir );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBaseAnimating :: GetAutomovement( Vector &origin, Vector &angles, float flInterval )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CBaseAnimating :: SetBodygroup( int iGroup, int iValue )
|
||||
{
|
||||
::SetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup, iValue );
|
||||
}
|
||||
|
||||
int CBaseAnimating :: GetBodygroup( int iGroup )
|
||||
{
|
||||
return ::GetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup );
|
||||
}
|
||||
|
||||
int CBaseAnimating :: GetBoneCount( void )
|
||||
{
|
||||
return ::GetBoneCount( GET_MODEL_PTR(ENT(pev)) );
|
||||
}
|
||||
|
||||
void CBaseAnimating :: SetBones( float (*data)[3], int datasize )
|
||||
{
|
||||
::SetBones( GET_MODEL_PTR( ENT(pev) ), data, datasize );
|
||||
}
|
||||
|
||||
int CBaseAnimating :: ExtractBbox( int sequence, float *mins, float *maxs )
|
||||
{
|
||||
return ::ExtractBbox( GET_MODEL_PTR( ENT(pev) ), sequence, mins, maxs );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
|
||||
void CBaseAnimating :: SetSequenceBox( void )
|
||||
{
|
||||
Vector mins, maxs;
|
||||
|
||||
// Get sequence bbox
|
||||
if ( ExtractBbox( pev->sequence, mins, maxs ) )
|
||||
{
|
||||
// expand box for rotation
|
||||
// find min / max for rotations
|
||||
float yaw = pev->angles.y * (M_PI / 180.0);
|
||||
|
||||
Vector xvector, yvector;
|
||||
xvector.x = cos(yaw);
|
||||
xvector.y = sin(yaw);
|
||||
yvector.x = -sin(yaw);
|
||||
yvector.y = cos(yaw);
|
||||
Vector bounds[2];
|
||||
|
||||
bounds[0] = mins;
|
||||
bounds[1] = maxs;
|
||||
|
||||
Vector rmin( 9999, 9999, 9999 );
|
||||
Vector rmax( -9999, -9999, -9999 );
|
||||
Vector base, transformed;
|
||||
|
||||
for (int i = 0; i <= 1; i++ )
|
||||
{
|
||||
base.x = bounds[i].x;
|
||||
for ( int j = 0; j <= 1; j++ )
|
||||
{
|
||||
base.y = bounds[j].y;
|
||||
for ( int k = 0; k <= 1; k++ )
|
||||
{
|
||||
base.z = bounds[k].z;
|
||||
|
||||
// transform the point
|
||||
transformed.x = xvector.x*base.x + yvector.x*base.y;
|
||||
transformed.y = xvector.y*base.x + yvector.y*base.y;
|
||||
transformed.z = base.z;
|
||||
|
||||
if (transformed.x < rmin.x)
|
||||
rmin.x = transformed.x;
|
||||
if (transformed.x > rmax.x)
|
||||
rmax.x = transformed.x;
|
||||
if (transformed.y < rmin.y)
|
||||
rmin.y = transformed.y;
|
||||
if (transformed.y > rmax.y)
|
||||
rmax.y = transformed.y;
|
||||
if (transformed.z < rmin.z)
|
||||
rmin.z = transformed.z;
|
||||
if (transformed.z > rmax.z)
|
||||
rmax.z = transformed.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
rmin.z = 0;
|
||||
rmax.z = rmin.z + 1;
|
||||
UTIL_SetSize( pev, rmin, rmax );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,562 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "const.h"
|
||||
#include "studio_ref.h"
|
||||
|
||||
#ifndef ACTIVITY_H
|
||||
#include "activity.h"
|
||||
#endif
|
||||
|
||||
#include "activitymap.h"
|
||||
|
||||
#ifndef ANIMATION_H
|
||||
#include "animation.h"
|
||||
#endif
|
||||
|
||||
#ifndef SCRIPTEVENT_H
|
||||
#include "scriptevent.h"
|
||||
#endif
|
||||
|
||||
#ifndef ENGINECALLBACK_H
|
||||
#include "enginecallback.h"
|
||||
#endif
|
||||
|
||||
extern globalvars_t *gpGlobals;
|
||||
|
||||
#pragma warning( disable : 4244 )
|
||||
|
||||
|
||||
|
||||
int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
mins[0] = pseqdesc[ sequence ].bbmin[0];
|
||||
mins[1] = pseqdesc[ sequence ].bbmin[1];
|
||||
mins[2] = pseqdesc[ sequence ].bbmin[2];
|
||||
|
||||
maxs[0] = pseqdesc[ sequence ].bbmax[0];
|
||||
maxs[1] = pseqdesc[ sequence ].bbmax[1];
|
||||
maxs[2] = pseqdesc[ sequence ].bbmax[2];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int LookupActivity( void *pmodel, entvars_t *pev, int activity )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
int weighttotal = 0;
|
||||
int seq = ACTIVITY_NOT_AVAILABLE;
|
||||
for (int i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (pseqdesc[i].activity == activity)
|
||||
{
|
||||
weighttotal += pseqdesc[i].actweight;
|
||||
if (!weighttotal || RANDOM_LONG(0,weighttotal-1) < pseqdesc[i].actweight)
|
||||
seq = i;
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
|
||||
int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr )
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
int weight = 0;
|
||||
int seq = ACTIVITY_NOT_AVAILABLE;
|
||||
for (int i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (pseqdesc[i].activity == activity)
|
||||
{
|
||||
if ( pseqdesc[i].actweight > weight )
|
||||
{
|
||||
weight = pseqdesc[i].actweight;
|
||||
seq = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
void GetEyePosition ( void *pmodel, float *vecEyePosition )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
|
||||
if ( !pstudiohdr )
|
||||
{
|
||||
ALERT ( at_console, "GetEyePosition() Can't get pstudiohdr ptr!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
vecEyePosition = pstudiohdr->eyeposition;
|
||||
}
|
||||
|
||||
int LookupSequence( void *pmodel, const char *label )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
for (int i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (stricmp( pseqdesc[i].label, label ) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int IsSoundEvent( int eventNumber )
|
||||
{
|
||||
if ( eventNumber == SCRIPT_EVENT_SOUND || eventNumber == SCRIPT_EVENT_SOUND_VOICE )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SequencePrecache( void *pmodel, const char *pSequenceName )
|
||||
{
|
||||
int index = LookupSequence( pmodel, pSequenceName );
|
||||
if ( index >= 0 )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr || index >= pstudiohdr->numseq )
|
||||
return;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
dstudioevent_t *pevent;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + index;
|
||||
pevent = (dstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex);
|
||||
|
||||
for (int i = 0; i < pseqdesc->numevents; i++)
|
||||
{
|
||||
// Don't send client-side events to the server AI
|
||||
if ( pevent[i].event >= EVENT_CLIENT )
|
||||
continue;
|
||||
|
||||
// UNDONE: Add a callback to check to see if a sound is precached yet and don't allocate a copy
|
||||
// of it's name if it is.
|
||||
if ( IsSoundEvent( pevent[i].event ) )
|
||||
{
|
||||
if ( !strlen(pevent[i].options) )
|
||||
{
|
||||
ALERT( at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options );
|
||||
}
|
||||
|
||||
PRECACHE_SOUND( STRING( ALLOC_STRING(pevent[i].options)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
if (pev->sequence >= pstudiohdr->numseq)
|
||||
{
|
||||
*pflFrameRate = 0.0;
|
||||
*pflGroundSpeed = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
|
||||
if (pseqdesc->numframes > 1)
|
||||
{
|
||||
*pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1);
|
||||
*pflGroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] );
|
||||
*pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
*pflFrameRate = 256.0;
|
||||
*pflGroundSpeed = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int GetSequenceFlags( void *pmodel, entvars_t *pev )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq )
|
||||
return 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
|
||||
return pseqdesc->flags;
|
||||
}
|
||||
|
||||
|
||||
int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent )
|
||||
return 0;
|
||||
|
||||
int events = 0;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
dstudioevent_t *pevent;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
pevent = (dstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex);
|
||||
|
||||
if (pseqdesc->numevents == 0 || index > pseqdesc->numevents )
|
||||
return 0;
|
||||
|
||||
if (pseqdesc->numframes > 1)
|
||||
{
|
||||
flStart *= (pseqdesc->numframes - 1) / 256.0;
|
||||
flEnd *= (pseqdesc->numframes - 1) / 256.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
flStart = 0;
|
||||
flEnd = 1.0;
|
||||
}
|
||||
|
||||
for (; index < pseqdesc->numevents; index++)
|
||||
{
|
||||
// Don't send client-side events to the server AI
|
||||
if ( pevent[index].event >= EVENT_CLIENT )
|
||||
continue;
|
||||
|
||||
if ( (pevent[index].frame >= flStart && pevent[index].frame < flEnd) ||
|
||||
((pseqdesc->flags & STUDIO_LOOPING) && flEnd >= pseqdesc->numframes - 1 && pevent[index].frame < flEnd - pseqdesc->numframes + 1) )
|
||||
{
|
||||
pMonsterEvent->event = pevent[index].event;
|
||||
pMonsterEvent->options = pevent[index].options;
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float SetController( void *pmodel, entvars_t *pev, int iController, float flValue )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return flValue;
|
||||
|
||||
dstudiobonecontroller_t *pbonecontroller = (dstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex);
|
||||
|
||||
// find first controller that matches the index
|
||||
int i = 0;
|
||||
for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++)
|
||||
{
|
||||
if (pbonecontroller->index == iController)
|
||||
break;
|
||||
}
|
||||
if (i >= pstudiohdr->numbonecontrollers)
|
||||
return flValue;
|
||||
|
||||
// wrap 0..360 if it's a rotational controller
|
||||
|
||||
if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
|
||||
{
|
||||
// ugly hack, invert value if end < start
|
||||
if (pbonecontroller->end < pbonecontroller->start)
|
||||
flValue = -flValue;
|
||||
|
||||
// does the controller not wrap?
|
||||
if (pbonecontroller->start + 359.0 >= pbonecontroller->end)
|
||||
{
|
||||
if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180)
|
||||
flValue = flValue - 360;
|
||||
if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180)
|
||||
flValue = flValue + 360;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flValue > 360)
|
||||
flValue = flValue - (int)(flValue / 360.0) * 360.0;
|
||||
else if (flValue < 0)
|
||||
flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0;
|
||||
}
|
||||
}
|
||||
|
||||
int setting = 255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start);
|
||||
|
||||
if (setting < 0) setting = 0;
|
||||
if (setting > 255) setting = 255;
|
||||
pev->controller[iController] = setting;
|
||||
|
||||
return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start;
|
||||
}
|
||||
|
||||
|
||||
float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return flValue;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence;
|
||||
|
||||
if (pseqdesc->blendtype[iBlender] == 0)
|
||||
return flValue;
|
||||
|
||||
if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR))
|
||||
{
|
||||
// ugly hack, invert value if end < start
|
||||
if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender])
|
||||
flValue = -flValue;
|
||||
|
||||
// does the controller not wrap?
|
||||
if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender])
|
||||
{
|
||||
if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180)
|
||||
flValue = flValue - 360;
|
||||
if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180)
|
||||
flValue = flValue + 360;
|
||||
}
|
||||
}
|
||||
|
||||
int setting = 255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]);
|
||||
|
||||
if (setting < 0) setting = 0;
|
||||
if (setting > 255) setting = 255;
|
||||
|
||||
pev->blending[iBlender] = setting;
|
||||
|
||||
return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return iGoalAnim;
|
||||
|
||||
dstudioseqdesc_t *pseqdesc;
|
||||
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
|
||||
|
||||
// bail if we're going to or from a node 0
|
||||
if (pseqdesc[iEndingAnim].entrynode == 0 || pseqdesc[iGoalAnim].entrynode == 0)
|
||||
{
|
||||
return iGoalAnim;
|
||||
}
|
||||
|
||||
int iEndNode;
|
||||
|
||||
// ALERT( at_console, "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
|
||||
|
||||
if (*piDir > 0)
|
||||
{
|
||||
iEndNode = pseqdesc[iEndingAnim].exitnode;
|
||||
}
|
||||
else
|
||||
{
|
||||
iEndNode = pseqdesc[iEndingAnim].entrynode;
|
||||
}
|
||||
|
||||
if (iEndNode == pseqdesc[iGoalAnim].entrynode)
|
||||
{
|
||||
*piDir = 1;
|
||||
return iGoalAnim;
|
||||
}
|
||||
|
||||
byte *pTransition = ((byte *)pstudiohdr + pstudiohdr->transitionindex);
|
||||
|
||||
int iInternNode = pTransition[(iEndNode-1)*pstudiohdr->numtransitions + (pseqdesc[iGoalAnim].entrynode-1)];
|
||||
|
||||
if (iInternNode == 0)
|
||||
return iGoalAnim;
|
||||
|
||||
int i;
|
||||
|
||||
// look for someone going
|
||||
for (i = 0; i < pstudiohdr->numseq; i++)
|
||||
{
|
||||
if (pseqdesc[i].entrynode == iEndNode && pseqdesc[i].exitnode == iInternNode)
|
||||
{
|
||||
*piDir = 1;
|
||||
return i;
|
||||
}
|
||||
if (pseqdesc[i].nodeflags)
|
||||
{
|
||||
if (pseqdesc[i].exitnode == iEndNode && pseqdesc[i].entrynode == iInternNode)
|
||||
{
|
||||
*piDir = -1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALERT( at_console, "error in transition graph\n" );
|
||||
return iGoalAnim;
|
||||
}
|
||||
|
||||
void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return;
|
||||
|
||||
if (iGroup > pstudiohdr->numbodyparts)
|
||||
return;
|
||||
|
||||
dstudiobodyparts_t *pbodypart = (dstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup;
|
||||
|
||||
if (iValue >= pbodypart->nummodels)
|
||||
return;
|
||||
|
||||
int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels;
|
||||
|
||||
pev->body = (pev->body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
|
||||
}
|
||||
|
||||
|
||||
int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (! pstudiohdr)
|
||||
return 0;
|
||||
|
||||
if (iGroup > pstudiohdr->numbodyparts)
|
||||
return 0;
|
||||
|
||||
dstudiobodyparts_t *pbodypart = (dstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup;
|
||||
|
||||
if (pbodypart->nummodels <= 1)
|
||||
return 0;
|
||||
|
||||
int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels;
|
||||
|
||||
return iCurrent;
|
||||
}
|
||||
|
||||
//LRC
|
||||
int GetBoneCount( void *pmodel )
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (!pstudiohdr)
|
||||
{
|
||||
ALERT(at_error, "Bad header in SetBones!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pstudiohdr->numbones;
|
||||
}
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
//LRC
|
||||
void SetBones( void *pmodel, float (*data)[3], int datasize)
|
||||
{
|
||||
dstudiohdr_t *pstudiohdr;
|
||||
|
||||
pstudiohdr = (dstudiohdr_t *)pmodel;
|
||||
if (!pstudiohdr)
|
||||
{
|
||||
ALERT(at_error, "Bad header in SetBones!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dstudiobone_t *pbone = (dstudiobone_t *)((byte *)pstudiohdr + pstudiohdr->boneindex);
|
||||
|
||||
// ALERT(at_console, "List begins:\n");
|
||||
int j;
|
||||
int limit = min(pstudiohdr->numbones, datasize);
|
||||
// go through the bones
|
||||
for (int i = 0; i < limit; i++, pbone++)
|
||||
{
|
||||
// ALERT(at_console, " %s\n", pbone->name);
|
||||
for (j = 0; j < 3; j++)
|
||||
pbone->value[j] = data[i][j];
|
||||
}
|
||||
// ALERT(at_console, "List ends.\n");
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#ifndef ANIMATION_H
|
||||
#define ANIMATION_H
|
||||
|
||||
#define ACTIVITY_NOT_AVAILABLE -1
|
||||
|
||||
#ifndef MONSTEREVENT_H
|
||||
#include "monsterevent.h"
|
||||
#endif
|
||||
|
||||
extern int IsSoundEvent( int eventNumber );
|
||||
|
||||
int LookupActivity( void *pmodel, entvars_t *pev, int activity );
|
||||
int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity );
|
||||
int LookupSequence( void *pmodel, const char *label );
|
||||
void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed );
|
||||
int GetSequenceFlags( void *pmodel, entvars_t *pev );
|
||||
int LookupAnimationEvents( void *pmodel, entvars_t *pev, float flStart, float flEnd );
|
||||
float SetController( void *pmodel, entvars_t *pev, int iController, float flValue );
|
||||
float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue );
|
||||
void GetEyePosition( void *pmodel, float *vecEyePosition );
|
||||
void SequencePrecache( void *pmodel, const char *pSequenceName );
|
||||
int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir );
|
||||
void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue );
|
||||
int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup );
|
||||
|
||||
//LRC
|
||||
void SetBones( void *pmodel, float (*data)[3], int datasize );
|
||||
int GetBoneCount( void *pmodel );
|
||||
int GetSequenceFrames( void *pmodel, entvars_t *pev ); //LRC
|
||||
|
||||
int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index );
|
||||
int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs );
|
||||
|
||||
// From /engine/studio.h
|
||||
#define STUDIO_LOOPING 0x0001
|
||||
|
||||
|
||||
#endif //ANIMATION_H
|
|
@ -0,0 +1,942 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#ifndef OEM_BUILD
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "client.h"
|
||||
#include "monsters.h"
|
||||
#include "baseweapon.h"
|
||||
#include "nodes.h"
|
||||
#include "basebeams.h"
|
||||
#include "defaults.h"
|
||||
|
||||
#define SF_WAITFORTRIGGER (0x04 | 0x40) // UNDONE: Fix!
|
||||
#define SF_NOWRECKAGE 0x08
|
||||
|
||||
class CApache : public CBaseMonster
|
||||
{
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify( void ) { return CLASS_HUMAN_MILITARY; };
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void GibMonster( void );
|
||||
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
pev->absmin = pev->origin + Vector( -300, -300, -172);
|
||||
pev->absmax = pev->origin + Vector(300, 300, 8);
|
||||
}
|
||||
|
||||
void EXPORT HuntThink( void );
|
||||
void EXPORT FlyTouch( CBaseEntity *pOther );
|
||||
void EXPORT CrashTouch( CBaseEntity *pOther );
|
||||
void EXPORT DyingThink( void );
|
||||
void EXPORT StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT NullThink( void );
|
||||
|
||||
void ShowDamage( void );
|
||||
void Flight( void );
|
||||
void FireRocket( void );
|
||||
BOOL FireGun( void );
|
||||
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
|
||||
int m_iRockets;
|
||||
float m_flForce;
|
||||
float m_flNextRocket;
|
||||
|
||||
Vector m_vecTarget;
|
||||
Vector m_posTarget;
|
||||
|
||||
Vector m_vecDesired;
|
||||
Vector m_posDesired;
|
||||
|
||||
Vector m_vecGoal;
|
||||
|
||||
Vector m_angGun;
|
||||
float m_flLastSeen;
|
||||
float m_flPrevSeen;
|
||||
|
||||
int m_iSoundState; // don't save this
|
||||
|
||||
int m_iSpriteTexture;
|
||||
int m_iExplode;
|
||||
int m_iBodyGibs;
|
||||
|
||||
float m_flGoalSpeed;
|
||||
|
||||
int m_iDoSmokePuff;
|
||||
CBeam *m_pBeam;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_apache, CApache );
|
||||
|
||||
TYPEDESCRIPTION CApache::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CApache, m_iRockets, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CApache, m_flForce, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CApache, m_flNextRocket, FIELD_TIME ),
|
||||
DEFINE_FIELD( CApache, m_vecTarget, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CApache, m_posTarget, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( CApache, m_vecDesired, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CApache, m_posDesired, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( CApache, m_vecGoal, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CApache, m_angGun, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( CApache, m_flLastSeen, FIELD_TIME ),
|
||||
DEFINE_FIELD( CApache, m_flPrevSeen, FIELD_TIME ),
|
||||
// DEFINE_FIELD( CApache, m_iSoundState, FIELD_INTEGER ), // Don't save, precached
|
||||
// DEFINE_FIELD( CApache, m_iSpriteTexture, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( CApache, m_iExplode, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( CApache, m_iBodyGibs, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( CApache, m_pBeam, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( CApache, m_flGoalSpeed, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CApache, m_iDoSmokePuff, FIELD_INTEGER ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CApache, CBaseMonster );
|
||||
|
||||
|
||||
void CApache :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/apache.mdl");
|
||||
UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) );
|
||||
UTIL_SetOrigin( this, pev->origin );
|
||||
|
||||
pev->flags |= FL_MONSTER;
|
||||
pev->takedamage = DAMAGE_AIM;
|
||||
if (pev->health == 0)
|
||||
pev->health = APACHE_HEALTH;
|
||||
|
||||
m_flFieldOfView = -0.707; // 270 degrees
|
||||
|
||||
pev->sequence = 0;
|
||||
ResetSequenceInfo( );
|
||||
pev->frame = RANDOM_LONG(0, 0xFF);
|
||||
|
||||
InitBoneControllers();
|
||||
|
||||
if (pev->spawnflags & SF_WAITFORTRIGGER)
|
||||
{
|
||||
SetUse(&CApache :: StartupUse );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetThink(&CApache :: HuntThink );
|
||||
SetTouch(&CApache :: FlyTouch );
|
||||
SetNextThink( 1.0 );
|
||||
}
|
||||
|
||||
m_iRockets = 10;
|
||||
}
|
||||
|
||||
|
||||
void CApache::Precache( void )
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/apache.mdl");
|
||||
|
||||
PRECACHE_SOUND("apache/ap_rotor1.wav");
|
||||
PRECACHE_SOUND("apache/ap_rotor2.wav");
|
||||
PRECACHE_SOUND("apache/ap_rotor3.wav");
|
||||
PRECACHE_SOUND("apache/ap_whine1.wav");
|
||||
|
||||
PRECACHE_SOUND("weapons/mortarhit.wav");
|
||||
|
||||
m_iSpriteTexture = PRECACHE_MODEL( "sprites/white.spr" );
|
||||
|
||||
PRECACHE_SOUND("turret/tu_fire1.wav");
|
||||
|
||||
PRECACHE_MODEL("sprites/lgtning.spr");
|
||||
|
||||
m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" );
|
||||
m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" );
|
||||
|
||||
UTIL_PrecacheEntity( "hvr_rocket" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CApache::NullThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( 0.5 );
|
||||
}
|
||||
|
||||
|
||||
void CApache::StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
SetThink(&CApache:: HuntThink );
|
||||
SetTouch(&CApache:: FlyTouch );
|
||||
SetNextThink( 0.1 );
|
||||
SetUse( NULL );
|
||||
}
|
||||
|
||||
void CApache :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->gravity = 0.3;
|
||||
|
||||
STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav" );
|
||||
|
||||
UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) );
|
||||
SetThink(&CApache :: DyingThink );
|
||||
SetTouch(&CApache :: CrashTouch );
|
||||
SetNextThink( 0.1 );
|
||||
pev->health = 0;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
if (pev->spawnflags & SF_NOWRECKAGE)
|
||||
{
|
||||
m_flNextRocket = gpGlobals->time + 4.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flNextRocket = gpGlobals->time + 15.0;
|
||||
}
|
||||
}
|
||||
|
||||
void CApache :: DyingThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
pev->avelocity = pev->avelocity * 1.02;
|
||||
|
||||
// still falling?
|
||||
if (m_flNextRocket > gpGlobals->time )
|
||||
{
|
||||
// random explosions
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, pev->origin );
|
||||
WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now
|
||||
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 ));
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( RANDOM_LONG(0,29) + 30 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
|
||||
// lots of smoke
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, pev->origin );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 ));
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 100 ); // scale * 10
|
||||
WRITE_BYTE( 10 ); // framerate
|
||||
MESSAGE_END();
|
||||
|
||||
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z );
|
||||
|
||||
// size
|
||||
WRITE_COORD( 400 );
|
||||
WRITE_COORD( 400 );
|
||||
WRITE_COORD( 132 );
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( pev->velocity.x );
|
||||
WRITE_COORD( pev->velocity.y );
|
||||
WRITE_COORD( pev->velocity.z );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 50 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_iBodyGibs ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 4 ); // let client decide
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 30 );// 3.0 seconds
|
||||
|
||||
// flags
|
||||
|
||||
WRITE_BYTE( BREAK_METAL );
|
||||
MESSAGE_END();
|
||||
|
||||
// don't stop it we touch a entity
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
SetNextThink( 0.2 );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
|
||||
/*
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
||||
WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 300 );
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 8 ); // framerate
|
||||
MESSAGE_END();
|
||||
*/
|
||||
|
||||
// fireball
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_SPRITE );
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 256 );
|
||||
WRITE_SHORT( m_iExplode );
|
||||
WRITE_BYTE( 120 ); // scale * 10
|
||||
WRITE_BYTE( 255 ); // brightness
|
||||
MESSAGE_END();
|
||||
|
||||
// big smoke
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 512 );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 5 ); // framerate
|
||||
MESSAGE_END();
|
||||
|
||||
// blast circle
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, pev->origin );
|
||||
WRITE_BYTE( TE_BEAMCYLINDER );
|
||||
WRITE_COORD( pev->origin.x);
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z);
|
||||
WRITE_COORD( pev->origin.x);
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z + 2000 ); // reach damage radius over .2 seconds
|
||||
WRITE_SHORT( m_iSpriteTexture );
|
||||
WRITE_BYTE( 0 ); // startframe
|
||||
WRITE_BYTE( 0 ); // framerate
|
||||
WRITE_BYTE( 4 ); // life
|
||||
WRITE_BYTE( 32 ); // width
|
||||
WRITE_BYTE( 0 ); // noise
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 192 ); // r, g, b
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
WRITE_BYTE( 0 ); // speed
|
||||
MESSAGE_END();
|
||||
|
||||
EMIT_SOUND(ENT(pev), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3);
|
||||
|
||||
RadiusDamage( pev->origin, pev, pev, 300, CLASS_NONE, DMG_BLAST );
|
||||
|
||||
if (pev->flags & FL_ONGROUND)
|
||||
{
|
||||
CBaseEntity *pWreckage = Create( "smokeent", pev->origin, pev->angles );
|
||||
UTIL_SetModel( ENT(pWreckage->pev), pev->model );
|
||||
UTIL_SetSize( pWreckage->pev, Vector( -200, -200, -128 ), Vector( 200, 200, -32 ) );
|
||||
pWreckage->SetNextThink( 0.1 );
|
||||
pWreckage->pev->frame = pev->frame;
|
||||
pWreckage->pev->sequence = pev->sequence;
|
||||
pWreckage->pev->framerate = 0;
|
||||
pWreckage->pev->impulse = 50;
|
||||
pWreckage->pev->dmgtime = gpGlobals->time + 5;
|
||||
}
|
||||
|
||||
// gibs
|
||||
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 64);
|
||||
|
||||
// size
|
||||
WRITE_COORD( 400 );
|
||||
WRITE_COORD( 400 );
|
||||
WRITE_COORD( 128 );
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( 0 );
|
||||
WRITE_COORD( 0 );
|
||||
WRITE_COORD( 200 );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 30 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_iBodyGibs ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 200 );
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 200 );// 10.0 seconds
|
||||
|
||||
// flags
|
||||
|
||||
WRITE_BYTE( BREAK_METAL );
|
||||
MESSAGE_END();
|
||||
|
||||
SetThink( Remove );
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CApache::FlyTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// bounce if we hit something solid
|
||||
if ( pOther->pev->solid == SOLID_BSP)
|
||||
{
|
||||
TraceResult tr = UTIL_GetGlobalTrace( );
|
||||
|
||||
// UNDONE, do a real bounce
|
||||
pev->velocity = pev->velocity + tr.vecPlaneNormal * (pev->velocity.Length() + 200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CApache::CrashTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// only crash if we hit something solid
|
||||
if ( pOther->pev->solid == SOLID_BSP)
|
||||
{
|
||||
SetTouch( NULL );
|
||||
m_flNextRocket = gpGlobals->time;
|
||||
SetNextThink( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CApache :: GibMonster( void )
|
||||
{
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "common/bodysplat.wav", 0.75, ATTN_NORM, 0, 200);
|
||||
}
|
||||
|
||||
|
||||
void CApache :: HuntThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
ShowDamage( );
|
||||
|
||||
if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target
|
||||
{
|
||||
m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) );
|
||||
if (m_pGoalEnt)
|
||||
{
|
||||
m_posDesired = m_pGoalEnt->pev->origin;
|
||||
UTIL_MakeAimVectors( m_pGoalEnt->pev->angles );
|
||||
m_vecGoal = gpGlobals->v_forward;
|
||||
}
|
||||
}
|
||||
|
||||
// if (m_hEnemy == NULL)
|
||||
{
|
||||
Look( 4092 );
|
||||
m_hEnemy = BestVisibleEnemy( );
|
||||
}
|
||||
|
||||
// generic speed up
|
||||
if (m_flGoalSpeed < 800)
|
||||
m_flGoalSpeed += 5;
|
||||
|
||||
if (m_hEnemy != NULL)
|
||||
{
|
||||
// ALERT( at_console, "%s\n", STRING( m_hEnemy->pev->classname ) );
|
||||
if (FVisible( m_hEnemy ))
|
||||
{
|
||||
if (m_flLastSeen < gpGlobals->time - 5)
|
||||
m_flPrevSeen = gpGlobals->time;
|
||||
m_flLastSeen = gpGlobals->time;
|
||||
m_posTarget = m_hEnemy->Center( );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hEnemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_vecTarget = (m_posTarget - pev->origin).Normalize();
|
||||
|
||||
float flLength = (pev->origin - m_posDesired).Length();
|
||||
|
||||
if (m_pGoalEnt)
|
||||
{
|
||||
// ALERT( at_console, "%.0f\n", flLength );
|
||||
|
||||
if (flLength < 128)
|
||||
{
|
||||
m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_pGoalEnt->pev->target ) );
|
||||
if (m_pGoalEnt)
|
||||
{
|
||||
m_posDesired = m_pGoalEnt->pev->origin;
|
||||
UTIL_MakeAimVectors( m_pGoalEnt->pev->angles );
|
||||
m_vecGoal = gpGlobals->v_forward;
|
||||
flLength = (pev->origin - m_posDesired).Length();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_posDesired = pev->origin;
|
||||
}
|
||||
|
||||
if (flLength > 250) // 500
|
||||
{
|
||||
// float flLength2 = (m_posTarget - pev->origin).Length() * (1.5 - DotProduct((m_posTarget - pev->origin).Normalize(), pev->velocity.Normalize() ));
|
||||
// if (flLength2 < flLength)
|
||||
if (m_flLastSeen + 90 > gpGlobals->time && DotProduct( (m_posTarget - pev->origin).Normalize(), (m_posDesired - pev->origin).Normalize( )) > 0.25)
|
||||
{
|
||||
m_vecDesired = (m_posTarget - pev->origin).Normalize( );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecDesired = (m_posDesired - pev->origin).Normalize( );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecDesired = m_vecGoal;
|
||||
}
|
||||
|
||||
Flight( );
|
||||
|
||||
// ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->time, m_flLastSeen, m_flPrevSeen );
|
||||
if ((m_flLastSeen + 1 > gpGlobals->time) && (m_flPrevSeen + 2 < gpGlobals->time))
|
||||
{
|
||||
if (FireGun( ))
|
||||
{
|
||||
// slow down if we're fireing
|
||||
if (m_flGoalSpeed > 400)
|
||||
m_flGoalSpeed = 400;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
Vector vecEst = (gpGlobals->v_forward * 800 + pev->velocity).Normalize( );
|
||||
// ALERT( at_console, "%d %d %d %4.2f\n", pev->angles.x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->time, DotProduct( m_vecTarget, vecEst ) );
|
||||
|
||||
if ((m_iRockets % 2) == 1)
|
||||
{
|
||||
FireRocket( );
|
||||
m_flNextRocket = gpGlobals->time + 0.5;
|
||||
if (m_iRockets <= 0)
|
||||
{
|
||||
m_flNextRocket = gpGlobals->time + 10;
|
||||
m_iRockets = 10;
|
||||
}
|
||||
}
|
||||
else if (pev->angles.x < 0 && DotProduct( pev->velocity, gpGlobals->v_forward ) > -100 && m_flNextRocket < gpGlobals->time)
|
||||
{
|
||||
if (m_flLastSeen + 60 > gpGlobals->time)
|
||||
{
|
||||
if (m_hEnemy != NULL)
|
||||
{
|
||||
// make sure it's a good shot
|
||||
if (DotProduct( m_vecTarget, vecEst) > .965)
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, ignore_monsters, edict(), &tr );
|
||||
if ((tr.vecEndPos - m_posTarget).Length() < 512)
|
||||
FireRocket( );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, dont_ignore_monsters, edict(), &tr );
|
||||
// just fire when close
|
||||
if ((tr.vecEndPos - m_posTarget).Length() < 512)
|
||||
FireRocket( );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CApache :: Flight( void )
|
||||
{
|
||||
// tilt model 5 degrees
|
||||
Vector vecAdj = Vector( 5.0, 0, 0 );
|
||||
|
||||
// estimate where I'll be facing in one seconds
|
||||
UTIL_MakeAimVectors( pev->angles + pev->avelocity * 2 + vecAdj);
|
||||
// Vector vecEst1 = pev->origin + pev->velocity + gpGlobals->v_up * m_flForce - Vector( 0, 0, 384 );
|
||||
// float flSide = DotProduct( m_posDesired - vecEst1, gpGlobals->v_right );
|
||||
|
||||
float flSide = DotProduct( m_vecDesired, gpGlobals->v_right );
|
||||
|
||||
if (flSide < 0)
|
||||
{
|
||||
if (pev->avelocity.y < 60)
|
||||
{
|
||||
pev->avelocity.y += 8; // 9 * (3.0/2.0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pev->avelocity.y > -60)
|
||||
{
|
||||
pev->avelocity.y -= 8; // 9 * (3.0/2.0);
|
||||
}
|
||||
}
|
||||
pev->avelocity.y *= 0.98;
|
||||
|
||||
// estimate where I'll be in two seconds
|
||||
UTIL_MakeAimVectors( pev->angles + pev->avelocity * 1 + vecAdj);
|
||||
Vector vecEst = pev->origin + pev->velocity * 2.0 + gpGlobals->v_up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
|
||||
|
||||
// add immediate force
|
||||
UTIL_MakeAimVectors( pev->angles + vecAdj);
|
||||
pev->velocity.x += gpGlobals->v_up.x * m_flForce;
|
||||
pev->velocity.y += gpGlobals->v_up.y * m_flForce;
|
||||
pev->velocity.z += gpGlobals->v_up.z * m_flForce;
|
||||
// add gravity
|
||||
pev->velocity.z -= 38.4; // 32ft/sec
|
||||
|
||||
|
||||
float flSpeed = pev->velocity.Length();
|
||||
float flDir = DotProduct( Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, 0 ), Vector( pev->velocity.x, pev->velocity.y, 0 ) );
|
||||
if (flDir < 0)
|
||||
flSpeed = -flSpeed;
|
||||
|
||||
float flDist = DotProduct( m_posDesired - vecEst, gpGlobals->v_forward );
|
||||
|
||||
// float flSlip = DotProduct( pev->velocity, gpGlobals->v_right );
|
||||
float flSlip = -DotProduct( m_posDesired - vecEst, gpGlobals->v_right );
|
||||
|
||||
// fly sideways
|
||||
if (flSlip > 0)
|
||||
{
|
||||
if (pev->angles.z > -30 && pev->avelocity.z > -15)
|
||||
pev->avelocity.z -= 4;
|
||||
else
|
||||
pev->avelocity.z += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (pev->angles.z < 30 && pev->avelocity.z < 15)
|
||||
pev->avelocity.z += 4;
|
||||
else
|
||||
pev->avelocity.z -= 2;
|
||||
}
|
||||
|
||||
// sideways drag
|
||||
pev->velocity.x = pev->velocity.x * (1.0 - fabs( gpGlobals->v_right.x ) * 0.05);
|
||||
pev->velocity.y = pev->velocity.y * (1.0 - fabs( gpGlobals->v_right.y ) * 0.05);
|
||||
pev->velocity.z = pev->velocity.z * (1.0 - fabs( gpGlobals->v_right.z ) * 0.05);
|
||||
|
||||
// general drag
|
||||
pev->velocity = pev->velocity * 0.995;
|
||||
|
||||
// apply power to stay correct height
|
||||
if (m_flForce < 80 && vecEst.z < m_posDesired.z)
|
||||
{
|
||||
m_flForce += 12;
|
||||
}
|
||||
else if (m_flForce > 30)
|
||||
{
|
||||
if (vecEst.z > m_posDesired.z)
|
||||
m_flForce -= 8;
|
||||
}
|
||||
|
||||
// pitch forward or back to get to target
|
||||
if (flDist > 0 && flSpeed < m_flGoalSpeed /* && flSpeed < flDist */ && pev->angles.x + pev->avelocity.x > -40)
|
||||
{
|
||||
// ALERT( at_console, "F " );
|
||||
// lean forward
|
||||
pev->avelocity.x -= 12.0;
|
||||
}
|
||||
else if (flDist < 0 && flSpeed > -50 && pev->angles.x + pev->avelocity.x < 20)
|
||||
{
|
||||
// ALERT( at_console, "B " );
|
||||
// lean backward
|
||||
pev->avelocity.x += 12.0;
|
||||
}
|
||||
else if (pev->angles.x + pev->avelocity.x > 0)
|
||||
{
|
||||
// ALERT( at_console, "f " );
|
||||
pev->avelocity.x -= 4.0;
|
||||
}
|
||||
else if (pev->angles.x + pev->avelocity.x < 0)
|
||||
{
|
||||
// ALERT( at_console, "b " );
|
||||
pev->avelocity.x += 4.0;
|
||||
}
|
||||
|
||||
// ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", pev->origin.x, pev->velocity.x, flDist, flSpeed, pev->angles.x, pev->avelocity.x, m_flForce );
|
||||
// ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", pev->origin.z, pev->velocity.z, vecEst.z, m_posDesired.z, m_flForce );
|
||||
|
||||
// make rotor, engine sounds
|
||||
if (m_iSoundState == 0)
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav", 1.0, 0.3, 0, 110 );
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", 0.5, 0.2, 0, 110 );
|
||||
|
||||
m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
|
||||
}
|
||||
else
|
||||
{
|
||||
CBaseEntity *pPlayer = NULL;
|
||||
|
||||
pPlayer = UTIL_FindEntityByClassname( NULL, "player" );
|
||||
// UNDONE: this needs to send different sounds to every player for multiplayer.
|
||||
if (pPlayer)
|
||||
{
|
||||
|
||||
float pitch = DotProduct( pev->velocity - pPlayer->pev->velocity, (pPlayer->pev->origin - pev->origin).Normalize() );
|
||||
|
||||
pitch = (int)(100 + pitch / 50.0);
|
||||
|
||||
if (pitch > 250)
|
||||
pitch = 250;
|
||||
if (pitch < 50)
|
||||
pitch = 50;
|
||||
if (pitch == 100)
|
||||
pitch = 101;
|
||||
|
||||
float flVol = (m_flForce / 100.0) + .1;
|
||||
if (flVol > 1.0)
|
||||
flVol = 1.0;
|
||||
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav", 1.0, 0.3, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
}
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", flVol, 0.2, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
|
||||
// ALERT( at_console, "%.0f %.2f\n", pitch, flVol );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CApache :: FireRocket( void )
|
||||
{
|
||||
static float side = 1.0;
|
||||
static int count;
|
||||
|
||||
if (m_iRockets <= 0)
|
||||
return;
|
||||
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
Vector vecSrc = pev->origin + 1.5 * (gpGlobals->v_forward * 21 + gpGlobals->v_right * 70 * side + gpGlobals->v_up * -79);
|
||||
|
||||
switch( m_iRockets % 5)
|
||||
{
|
||||
case 0: vecSrc = vecSrc + gpGlobals->v_right * 10; break;
|
||||
case 1: vecSrc = vecSrc - gpGlobals->v_right * 10; break;
|
||||
case 2: vecSrc = vecSrc + gpGlobals->v_up * 10; break;
|
||||
case 3: vecSrc = vecSrc - gpGlobals->v_up * 10; break;
|
||||
case 4: break;
|
||||
}
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSrc );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSrc.x );
|
||||
WRITE_COORD( vecSrc.y );
|
||||
WRITE_COORD( vecSrc.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 20 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
|
||||
CBaseEntity *pRocket = CBaseEntity::Create( "hvr_rocket", vecSrc, pev->angles, edict() );
|
||||
if (pRocket)
|
||||
pRocket->pev->velocity = pev->velocity + gpGlobals->v_forward * 100;
|
||||
|
||||
m_iRockets--;
|
||||
|
||||
side = - side;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL CApache :: FireGun( )
|
||||
{
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
Vector posGun, angGun;
|
||||
GetAttachment( 1, posGun, angGun );
|
||||
|
||||
Vector vecTarget = (m_posTarget - posGun).Normalize( );
|
||||
|
||||
Vector vecOut;
|
||||
|
||||
vecOut.x = DotProduct( gpGlobals->v_forward, vecTarget );
|
||||
vecOut.y = -DotProduct( gpGlobals->v_right, vecTarget );
|
||||
vecOut.z = DotProduct( gpGlobals->v_up, vecTarget );
|
||||
|
||||
Vector angles = UTIL_VecToAngles (vecOut);
|
||||
|
||||
angles.x = -angles.x;
|
||||
if (angles.y > 180)
|
||||
angles.y = angles.y - 360;
|
||||
if (angles.y < -180)
|
||||
angles.y = angles.y + 360;
|
||||
if (angles.x > 180)
|
||||
angles.x = angles.x - 360;
|
||||
if (angles.x < -180)
|
||||
angles.x = angles.x + 360;
|
||||
|
||||
if (angles.x > m_angGun.x)
|
||||
m_angGun.x = min( angles.x, m_angGun.x + 12 );
|
||||
if (angles.x < m_angGun.x)
|
||||
m_angGun.x = max( angles.x, m_angGun.x - 12 );
|
||||
if (angles.y > m_angGun.y)
|
||||
m_angGun.y = min( angles.y, m_angGun.y + 12 );
|
||||
if (angles.y < m_angGun.y)
|
||||
m_angGun.y = max( angles.y, m_angGun.y - 12 );
|
||||
|
||||
m_angGun.y = SetBoneController( 0, m_angGun.y );
|
||||
m_angGun.x = SetBoneController( 1, m_angGun.x );
|
||||
|
||||
Vector posBarrel, angBarrel;
|
||||
GetAttachment( 0, posBarrel, angBarrel );
|
||||
Vector vecGun = (posBarrel - posGun).Normalize( );
|
||||
|
||||
if (DotProduct( vecGun, vecTarget ) > 0.98)
|
||||
{
|
||||
#if 1
|
||||
FireBullets( 1, posGun, vecGun, VECTOR_CONE_4DEGREES, 8192, BULLET_12MM, 1 );
|
||||
EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.3);
|
||||
#else
|
||||
static float flNext;
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( posGun, posGun + vecGun * 8192, dont_ignore_monsters, ENT( pev ), &tr );
|
||||
|
||||
if (!m_pBeam)
|
||||
{
|
||||
m_pBeam = CBeam::BeamCreate( "sprites/lgtning.spr", 80 );
|
||||
m_pBeam->PointEntInit( pev->origin, entindex( ) );
|
||||
m_pBeam->SetEndAttachment( 1 );
|
||||
m_pBeam->SetColor( 255, 180, 96 );
|
||||
m_pBeam->SetBrightness( 192 );
|
||||
}
|
||||
|
||||
if (flNext < gpGlobals->time)
|
||||
{
|
||||
flNext = gpGlobals->time + 0.5;
|
||||
m_pBeam->SetStartPos( tr.vecEndPos );
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_pBeam)
|
||||
{
|
||||
UTIL_Remove( m_pBeam );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CApache :: ShowDamage( void )
|
||||
{
|
||||
if (m_iDoSmokePuff > 0 || RANDOM_LONG(0,99) > pev->health)
|
||||
{
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, pev->origin );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( pev->origin.x );
|
||||
WRITE_COORD( pev->origin.y );
|
||||
WRITE_COORD( pev->origin.z - 32 );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
}
|
||||
if (m_iDoSmokePuff > 0)
|
||||
m_iDoSmokePuff--;
|
||||
}
|
||||
|
||||
|
||||
int CApache :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if (pevInflictor->owner == edict())
|
||||
return 0;
|
||||
|
||||
if (bitsDamageType & DMG_BLAST)
|
||||
{
|
||||
flDamage *= 2;
|
||||
}
|
||||
|
||||
/*
|
||||
if ( (bitsDamageType & DMG_BULLET) && flDamage > 50)
|
||||
{
|
||||
// clip bullet damage at 50
|
||||
flDamage = 50;
|
||||
}
|
||||
*/
|
||||
|
||||
// ALERT( at_console, "%.0f\n", flDamage );
|
||||
return CBaseEntity::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CApache::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
// ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage );
|
||||
|
||||
// ignore blades
|
||||
if (ptr->iHitgroup == 6 && (bitsDamageType & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB)))
|
||||
return;
|
||||
|
||||
// hit hard, hits cockpit, hits engines
|
||||
if (flDamage > 50 || ptr->iHitgroup == 1 || ptr->iHitgroup == 2)
|
||||
{
|
||||
// ALERT( at_console, "%.0f\n", flDamage );
|
||||
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
||||
m_iDoSmokePuff = 3 + (flDamage / 5.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// do half damage in the body
|
||||
// AddMultiDamage( pevAttacker, this, flDamage / 2.0, bitsDamageType );
|
||||
UTIL_Ricochet( ptr->vecEndPos, 2.0 );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,434 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// barnacle - stationary ceiling mounted 'fishing' monster
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
|
||||
#define BARNACLE_BODY_HEIGHT 44 // how 'tall' the barnacle's model is.
|
||||
#define BARNACLE_PULL_SPEED 8
|
||||
#define BARNACLE_KILL_VICTIM_DELAY 5 // how many seconds after pulling prey in to gib them.
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define BARNACLE_AE_PUKEGIB 2
|
||||
|
||||
class CBarnacle : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
CBaseEntity *TongueTouchEnt ( float *pflLength );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void EXPORT BarnacleThink ( void );
|
||||
void EXPORT WaitTillDead ( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
float m_flAltitude;
|
||||
float m_flKillVictimTime;
|
||||
int m_cGibs;// barnacle loads up on gibs each time it kills something.
|
||||
BOOL m_fTongueExtended;
|
||||
BOOL m_fLiftingPrey;
|
||||
float m_flTongueAdj;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_barnacle, CBarnacle );
|
||||
|
||||
TYPEDESCRIPTION CBarnacle::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBarnacle, m_flAltitude, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CBarnacle, m_flKillVictimTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBarnacle, m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
|
||||
DEFINE_FIELD( CBarnacle, m_fTongueExtended, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarnacle, m_fLiftingPrey, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarnacle, m_flTongueAdj, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBarnacle, CBaseMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CBarnacle :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CBarnacle :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BARNACLE_AE_PUKEGIB:
|
||||
CGib::SpawnRandomGibs( this, 1, 1 );
|
||||
break;
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CBarnacle :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/barnacle.mdl");
|
||||
UTIL_SetSize( pev, Vector(-16, -16, -32), Vector(16, 16, 0) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_NONE;
|
||||
pev->takedamage = DAMAGE_AIM;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->effects = EF_INVLIGHT; // take light from the ceiling
|
||||
pev->health = 25;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
m_flKillVictimTime = 0;
|
||||
m_cGibs = 0;
|
||||
m_fLiftingPrey = FALSE;
|
||||
m_flTongueAdj = -100;
|
||||
|
||||
InitBoneControllers();
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
SetThink(&CBarnacle :: BarnacleThink );
|
||||
SetNextThink( 0.5 );
|
||||
|
||||
UTIL_SetOrigin ( this, pev->origin );
|
||||
}
|
||||
|
||||
int CBarnacle::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if ( bitsDamageType & DMG_CLUB )
|
||||
{
|
||||
flDamage = pev->health;
|
||||
}
|
||||
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBarnacle :: BarnacleThink ( void )
|
||||
{
|
||||
CBaseEntity *pTouchEnt;
|
||||
CBaseMonster *pVictim;
|
||||
float flLength;
|
||||
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
// barnacle has prey.
|
||||
|
||||
if ( !m_hEnemy->IsAlive() )
|
||||
{
|
||||
// someone (maybe even the barnacle) killed the prey. Reset barnacle.
|
||||
m_fLiftingPrey = FALSE;// indicate that we're not lifting prey.
|
||||
m_hEnemy = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( m_fLiftingPrey )
|
||||
{
|
||||
if ( m_hEnemy != NULL && m_hEnemy->pev->deadflag != DEAD_NO )
|
||||
{
|
||||
// crap, someone killed the prey on the way up.
|
||||
m_hEnemy = NULL;
|
||||
m_fLiftingPrey = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
// still pulling prey.
|
||||
Vector vecNewEnemyOrigin = m_hEnemy->pev->origin;
|
||||
vecNewEnemyOrigin.x = pev->origin.x;
|
||||
vecNewEnemyOrigin.y = pev->origin.y;
|
||||
|
||||
// guess as to where their neck is
|
||||
vecNewEnemyOrigin.x -= 6 * cos(m_hEnemy->pev->angles.y * M_PI/180.0);
|
||||
vecNewEnemyOrigin.y -= 6 * sin(m_hEnemy->pev->angles.y * M_PI/180.0);
|
||||
|
||||
m_flAltitude -= BARNACLE_PULL_SPEED;
|
||||
vecNewEnemyOrigin.z += BARNACLE_PULL_SPEED;
|
||||
|
||||
if ( fabs( pev->origin.z - ( vecNewEnemyOrigin.z + m_hEnemy->pev->view_ofs.z - 8 ) ) < BARNACLE_BODY_HEIGHT )
|
||||
{
|
||||
// prey has just been lifted into position ( if the victim origin + eye height + 8 is higher
|
||||
// than the bottom of the barnacle, it is assumed that the head is within barnacle's body )
|
||||
m_fLiftingPrey = FALSE;
|
||||
|
||||
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_bite3.wav", 1, ATTN_NORM );
|
||||
|
||||
pVictim = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
m_flKillVictimTime = gpGlobals->time + 10;// now that the victim is in place, the killing bite will be administered in 10 seconds.
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->BarnacleVictimBitten( pev );
|
||||
SetActivity ( ACT_EAT );
|
||||
}
|
||||
}
|
||||
|
||||
UTIL_SetOrigin ( m_hEnemy, vecNewEnemyOrigin );
|
||||
}
|
||||
else
|
||||
{
|
||||
// prey is lifted fully into feeding position and is dangling there.
|
||||
|
||||
pVictim = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
if ( m_flKillVictimTime != -1 && gpGlobals->time > m_flKillVictimTime )
|
||||
{
|
||||
// kill!
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->TakeDamage ( pev, pev, pVictim->pev->health, DMG_SLASH | DMG_ALWAYSGIB );
|
||||
m_cGibs = 3;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// bite prey every once in a while
|
||||
if ( pVictim && ( RANDOM_LONG(0,49) == 0 ) )
|
||||
{
|
||||
switch ( RANDOM_LONG(0,2) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM ); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
|
||||
pVictim->BarnacleVictimBitten( pev );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// barnacle has no prey right now, so just idle and check to see if anything is touching the tongue.
|
||||
|
||||
// If idle and no nearby client, don't think so often
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() ) )
|
||||
SetNextThink( RANDOM_FLOAT(1,1.5) ); // Stagger a bit to keep barnacles from thinking on the same frame
|
||||
|
||||
if ( m_fSequenceFinished )
|
||||
{// this is done so barnacle will fidget.
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flTongueAdj = -100;
|
||||
}
|
||||
|
||||
if ( m_cGibs && RANDOM_LONG(0,99) == 1 )
|
||||
{
|
||||
// cough up a gib.
|
||||
CGib::SpawnRandomGibs( this, 1, 1 );
|
||||
m_cGibs--;
|
||||
|
||||
switch ( RANDOM_LONG(0,2) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew2.wav", 1, ATTN_NORM ); break;
|
||||
case 2: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_chew3.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
}
|
||||
|
||||
pTouchEnt = TongueTouchEnt( &flLength );
|
||||
|
||||
if ( pTouchEnt != NULL && m_fTongueExtended )
|
||||
{
|
||||
// tongue is fully extended, and is touching someone.
|
||||
if ( pTouchEnt->FBecomeProne() )
|
||||
{
|
||||
EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_alert2.wav", 1, ATTN_NORM );
|
||||
|
||||
SetSequenceByName ( "attack1" );
|
||||
m_flTongueAdj = -20;
|
||||
|
||||
m_hEnemy = pTouchEnt;
|
||||
|
||||
pTouchEnt->pev->movetype = MOVETYPE_FLY;
|
||||
pTouchEnt->pev->velocity = pev->velocity; //LRC- make him come _with_ me
|
||||
pTouchEnt->pev->origin.x = pev->origin.x;
|
||||
pTouchEnt->pev->origin.y = pev->origin.y;
|
||||
|
||||
m_fLiftingPrey = TRUE;// indicate that we should be lifting prey.
|
||||
m_flKillVictimTime = -1;// set this to a bogus time while the victim is lifted.
|
||||
|
||||
m_flAltitude = (pev->origin.z - pTouchEnt->EyePosition().z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate a new length for the tongue to be clear of anything else that moves under it.
|
||||
if ( m_flAltitude < flLength )
|
||||
{
|
||||
// if tongue is higher than is should be, lower it kind of slowly.
|
||||
m_flAltitude += BARNACLE_PULL_SPEED;
|
||||
m_fTongueExtended = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flAltitude = flLength;
|
||||
m_fTongueExtended = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ALERT( at_console, "tounge %f\n", m_flAltitude + m_flTongueAdj );
|
||||
SetBoneController( 0, -(m_flAltitude + m_flTongueAdj) );
|
||||
StudioFrameAdvance( 0.1 );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CBarnacle :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
CBaseMonster *pVictim;
|
||||
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
pVictim = m_hEnemy->MyMonsterPointer();
|
||||
|
||||
if ( pVictim )
|
||||
{
|
||||
pVictim->BarnacleVictimReleased();
|
||||
}
|
||||
}
|
||||
|
||||
// CGib::SpawnRandomGibs( pev, 4, 1 );
|
||||
|
||||
switch ( RANDOM_LONG ( 0, 1 ) )
|
||||
{
|
||||
case 0: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_die1.wav", 1, ATTN_NORM ); break;
|
||||
case 1: EMIT_SOUND( ENT(pev), CHAN_WEAPON, "barnacle/bcl_die3.wav", 1, ATTN_NORM ); break;
|
||||
}
|
||||
|
||||
SetActivity ( ACT_DIESIMPLE );
|
||||
SetBoneController( 0, 0 );
|
||||
|
||||
StudioFrameAdvance( 0.1 );
|
||||
|
||||
SetNextThink( 0.1 );
|
||||
SetThink(&CBarnacle :: WaitTillDead );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//=========================================================
|
||||
void CBarnacle :: WaitTillDead ( void )
|
||||
{
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
float flInterval = StudioFrameAdvance( 0.1 );
|
||||
DispatchAnimEvents ( flInterval );
|
||||
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
// death anim finished.
|
||||
StopAnimation();
|
||||
SetThink ( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CBarnacle :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/barnacle.mdl");
|
||||
|
||||
PRECACHE_SOUND("barnacle/bcl_alert2.wav");//happy, lifting food up
|
||||
PRECACHE_SOUND("barnacle/bcl_bite3.wav");//just got food to mouth
|
||||
PRECACHE_SOUND("barnacle/bcl_chew1.wav");
|
||||
PRECACHE_SOUND("barnacle/bcl_chew2.wav");
|
||||
PRECACHE_SOUND("barnacle/bcl_chew3.wav");
|
||||
PRECACHE_SOUND("barnacle/bcl_die1.wav" );
|
||||
PRECACHE_SOUND("barnacle/bcl_die3.wav" );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// TongueTouchEnt - does a trace along the barnacle's tongue
|
||||
// to see if any entity is touching it. Also stores the length
|
||||
// of the trace in the int pointer provided.
|
||||
//=========================================================
|
||||
#define BARNACLE_CHECK_SPACING 8
|
||||
CBaseEntity *CBarnacle :: TongueTouchEnt ( float *pflLength )
|
||||
{
|
||||
TraceResult tr;
|
||||
float length;
|
||||
|
||||
// trace once to hit architecture and see if the tongue needs to change position.
|
||||
UTIL_TraceLine ( pev->origin, pev->origin - Vector ( 0 , 0 , 2048 ), ignore_monsters, ENT(pev), &tr );
|
||||
length = fabs( pev->origin.z - tr.vecEndPos.z );
|
||||
if ( pflLength )
|
||||
{
|
||||
*pflLength = length;
|
||||
}
|
||||
|
||||
Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
|
||||
Vector mins = pev->origin - delta;
|
||||
Vector maxs = pev->origin + delta;
|
||||
maxs.z = pev->origin.z;
|
||||
mins.z -= length;
|
||||
|
||||
CBaseEntity *pList[10];
|
||||
int count = UTIL_EntitiesInBox( pList, 10, mins, maxs, (FL_CLIENT|FL_MONSTER) );
|
||||
if ( count )
|
||||
{
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
// only clients and monsters
|
||||
if ( pList[i] != this && IRelationship( pList[i] ) > R_NO && pList[ i ]->pev->deadflag == DEAD_NO ) // this ent is one of our enemies. Barnacle tries to eat it.
|
||||
{
|
||||
return pList[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,929 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// monster template
|
||||
//=========================================================
|
||||
// UNDONE: Holster weapon?
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "client.h"
|
||||
#include "monsters.h"
|
||||
#include "talkmonster.h"
|
||||
#include "schedule.h"
|
||||
#include "defaultai.h"
|
||||
#include "scripted.h"
|
||||
#include "baseweapon.h"
|
||||
#include "soundent.h"
|
||||
#include "defaults.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
// first flag is barney dying for scripted sequences?
|
||||
#define BARNEY_AE_DRAW ( 2 )
|
||||
#define BARNEY_AE_SHOOT ( 3 )
|
||||
#define BARNEY_AE_HOLSTER ( 4 )
|
||||
|
||||
#define BARNEY_BODY_GUNHOLSTERED 0
|
||||
#define BARNEY_BODY_GUNDRAWN 1
|
||||
#define BARNEY_BODY_GUNGONE 2
|
||||
|
||||
class CBarney : public CTalkMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int ISoundMask( void );
|
||||
void BarneyFirePistol( void );
|
||||
void AlertSound( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
void RunTask( Task_t *pTask );
|
||||
void StartTask( Task_t *pTask );
|
||||
virtual int ObjectCaps( void ) { return CTalkMonster :: ObjectCaps() | FCAP_IMPULSE_USE; }
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
|
||||
void DeclineFollowing( void );
|
||||
|
||||
// Override these to set behavior
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
Schedule_t *GetSchedule ( void );
|
||||
MONSTERSTATE GetIdealState ( void );
|
||||
|
||||
void DeathSound( void );
|
||||
void PainSound( void );
|
||||
|
||||
void TalkInit( void );
|
||||
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int m_iBaseBody; //LRC - for barneys with different bodies
|
||||
BOOL m_fGunDrawn;
|
||||
float m_painTime;
|
||||
float m_checkAttackTime;
|
||||
BOOL m_lastAttackCheck;
|
||||
|
||||
// UNDONE: What is this for? It isn't used?
|
||||
float m_flPlayerDamage;// how much pain has the player inflicted on me?
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barney, CBarney );
|
||||
|
||||
TYPEDESCRIPTION CBarney::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CBarney, m_iBaseBody, FIELD_INTEGER ), //LRC
|
||||
DEFINE_FIELD( CBarney, m_fGunDrawn, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarney, m_painTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBarney, m_checkAttackTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CBarney, m_lastAttackCheck, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CBarney, m_flPlayerDamage, FIELD_FLOAT ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CBarney, CTalkMonster );
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
Task_t tlBaFollow[] =
|
||||
{
|
||||
{ TASK_MOVE_TO_TARGET_RANGE,(float)128 }, // Move within 128 of target ent (client)
|
||||
{ TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE },
|
||||
};
|
||||
|
||||
Schedule_t slBaFollow[] =
|
||||
{
|
||||
{
|
||||
tlBaFollow,
|
||||
ARRAYSIZE ( tlBaFollow ),
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_PROVOKED,
|
||||
bits_SOUND_DANGER,
|
||||
"Follow"
|
||||
},
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// BarneyDraw- much better looking draw schedule for when
|
||||
// barney knows who he's gonna attack.
|
||||
//=========================================================
|
||||
Task_t tlBarneyEnemyDraw[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_FACE_ENEMY, 0 },
|
||||
{ TASK_PLAY_SEQUENCE_FACE_ENEMY, (float) ACT_ARM },
|
||||
};
|
||||
|
||||
Schedule_t slBarneyEnemyDraw[] =
|
||||
{
|
||||
{
|
||||
tlBarneyEnemyDraw,
|
||||
ARRAYSIZE ( tlBarneyEnemyDraw ),
|
||||
0,
|
||||
0,
|
||||
"Barney Enemy Draw"
|
||||
}
|
||||
};
|
||||
|
||||
Task_t tlBaFaceTarget[] =
|
||||
{
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_FACE_TARGET, (float)0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE },
|
||||
};
|
||||
|
||||
Schedule_t slBaFaceTarget[] =
|
||||
{
|
||||
{
|
||||
tlBaFaceTarget,
|
||||
ARRAYSIZE ( tlBaFaceTarget ),
|
||||
bits_COND_CLIENT_PUSH |
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_PROVOKED,
|
||||
bits_SOUND_DANGER,
|
||||
"FaceTarget"
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Task_t tlIdleBaStand[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, 0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds.
|
||||
{ TASK_TLK_HEADRESET, (float)0 }, // reset head position
|
||||
};
|
||||
|
||||
Schedule_t slIdleBaStand[] =
|
||||
{
|
||||
{
|
||||
tlIdleBaStand,
|
||||
ARRAYSIZE ( tlIdleBaStand ),
|
||||
bits_COND_NEW_ENEMY |
|
||||
bits_COND_LIGHT_DAMAGE |
|
||||
bits_COND_HEAVY_DAMAGE |
|
||||
bits_COND_HEAR_SOUND |
|
||||
bits_COND_SMELL |
|
||||
bits_COND_PROVOKED,
|
||||
|
||||
bits_SOUND_COMBAT |// sound flags - change these, and you'll break the talking code.
|
||||
//bits_SOUND_PLAYER |
|
||||
//bits_SOUND_WORLD |
|
||||
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_MEAT |// scents
|
||||
bits_SOUND_CARCASS |
|
||||
bits_SOUND_GARBAGE,
|
||||
"IdleStand"
|
||||
},
|
||||
};
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CBarney )
|
||||
{
|
||||
slBaFollow,
|
||||
slBarneyEnemyDraw,
|
||||
slBaFaceTarget,
|
||||
slIdleBaStand,
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CBarney, CTalkMonster );
|
||||
|
||||
void CBarney :: StartTask( Task_t *pTask )
|
||||
{
|
||||
CTalkMonster::StartTask( pTask );
|
||||
}
|
||||
|
||||
void CBarney :: RunTask( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
if (m_hEnemy != NULL && (m_hEnemy->IsPlayer()))
|
||||
{
|
||||
pev->framerate = 1.5;
|
||||
}
|
||||
CTalkMonster::RunTask( pTask );
|
||||
break;
|
||||
default:
|
||||
CTalkMonster::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - returns a bit mask indicating which types
|
||||
// of sounds this monster regards.
|
||||
//=========================================================
|
||||
int CBarney :: ISoundMask ( void)
|
||||
{
|
||||
return bits_SOUND_WORLD |
|
||||
bits_SOUND_COMBAT |
|
||||
bits_SOUND_CARCASS |
|
||||
bits_SOUND_MEAT |
|
||||
bits_SOUND_GARBAGE |
|
||||
bits_SOUND_DANGER |
|
||||
bits_SOUND_PLAYER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CBarney :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_PLAYER_ALLY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ALertSound - barney says "Freeze!"
|
||||
//=========================================================
|
||||
void CBarney :: AlertSound( void )
|
||||
{
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
if ( FOkToSpeak() )
|
||||
{
|
||||
if (m_iszSpeakAs)
|
||||
{
|
||||
char szBuf[32];
|
||||
strcpy(szBuf,STRING(m_iszSpeakAs));
|
||||
strcat(szBuf,"_ATTACK");
|
||||
PlaySentence( szBuf, RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySentence( "BA_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CBarney :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 0;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_WALK:
|
||||
ys = 70;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
ys = 90;
|
||||
break;
|
||||
default:
|
||||
ys = 70;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
BOOL CBarney :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( flDist <= 1024 && flDot >= 0.5 )
|
||||
{
|
||||
if ( gpGlobals->time > m_checkAttackTime )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
Vector shootOrigin = pev->origin + Vector( 0, 0, 55 );
|
||||
CBaseEntity *pEnemy = m_hEnemy;
|
||||
Vector shootTarget = ( (pEnemy->BodyTarget( shootOrigin ) - pEnemy->pev->origin) + m_vecEnemyLKP );
|
||||
UTIL_TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, ENT(pev), &tr );
|
||||
m_checkAttackTime = gpGlobals->time + 1;
|
||||
if ( tr.flFraction == 1.0 || (tr.pHit != NULL && CBaseEntity::Instance(tr.pHit) == pEnemy) )
|
||||
m_lastAttackCheck = TRUE;
|
||||
else
|
||||
m_lastAttackCheck = FALSE;
|
||||
m_checkAttackTime = gpGlobals->time + 1.5;
|
||||
}
|
||||
return m_lastAttackCheck;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// BarneyFirePistol - shoots one round from the pistol at
|
||||
// the enemy barney is facing.
|
||||
//=========================================================
|
||||
void CBarney :: BarneyFirePistol ( void )
|
||||
{
|
||||
Vector vecShootOrigin;
|
||||
|
||||
UTIL_MakeVectors(pev->angles);
|
||||
vecShootOrigin = pev->origin + Vector( 0, 0, 55 );
|
||||
Vector vecShootDir = ShootAtEnemy( vecShootOrigin );
|
||||
|
||||
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
||||
SetBlending( 0, angDir.x );
|
||||
pev->effects = EF_MUZZLEFLASH;
|
||||
|
||||
if (pev->frags)
|
||||
{
|
||||
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_357);
|
||||
if (RANDOM_LONG(0, 1))
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "weapons/357_shot1.wav", 1, ATTN_NORM, 0, 100 );
|
||||
else
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "weapons/357_shot2.wav", 1, ATTN_NORM, 0, 100 );
|
||||
}
|
||||
else
|
||||
{
|
||||
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_9MM );
|
||||
|
||||
int pitchShift = RANDOM_LONG( 0, 20 );
|
||||
|
||||
// Only shift about half the time
|
||||
if ( pitchShift > 10 )
|
||||
pitchShift = 0;
|
||||
else
|
||||
pitchShift -= 5;
|
||||
|
||||
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 384, 0.3 );
|
||||
|
||||
// UNDONE: Reload?
|
||||
m_cAmmoLoaded--;// take away a bullet!
|
||||
|
||||
// Teh_Freak: World Lighting!
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
||||
WRITE_BYTE( TE_DLIGHT );
|
||||
WRITE_COORD( vecShootOrigin.x ); // origin
|
||||
WRITE_COORD( vecShootOrigin.y );
|
||||
WRITE_COORD( vecShootOrigin.z );
|
||||
WRITE_BYTE( 16 ); // radius
|
||||
WRITE_BYTE( 255 ); // R
|
||||
WRITE_BYTE( 255 ); // G
|
||||
WRITE_BYTE( 128 ); // B
|
||||
WRITE_BYTE( 0 ); // life * 10
|
||||
WRITE_BYTE( 0 ); // decay
|
||||
MESSAGE_END();
|
||||
// Teh_Freak: World Lighting!
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//
|
||||
// Returns number of events handled, 0 if none.
|
||||
//=========================================================
|
||||
void CBarney :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case BARNEY_AE_SHOOT:
|
||||
BarneyFirePistol();
|
||||
break;
|
||||
|
||||
case BARNEY_AE_DRAW:
|
||||
// barney's bodygroup switches here so he can pull gun from holster
|
||||
pev->body = m_iBaseBody + BARNEY_BODY_GUNDRAWN;
|
||||
m_fGunDrawn = TRUE;
|
||||
break;
|
||||
|
||||
case BARNEY_AE_HOLSTER:
|
||||
// change bodygroup to replace gun in holster
|
||||
pev->body = m_iBaseBody + BARNEY_BODY_GUNHOLSTERED;
|
||||
m_fGunDrawn = FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
CTalkMonster::HandleAnimEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CBarney :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/barney.mdl");
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
if (pev->health == 0) //LRC
|
||||
pev->health = BARNEY_HEALTH;
|
||||
pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
m_iBaseBody = pev->body; //LRC
|
||||
pev->body = m_iBaseBody + BARNEY_BODY_GUNHOLSTERED; // gun in holster
|
||||
m_fGunDrawn = FALSE;
|
||||
|
||||
m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP;
|
||||
|
||||
MonsterInit();
|
||||
SetUse(&CBarney :: FollowerUse );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CBarney :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/barney.mdl");
|
||||
|
||||
PRECACHE_SOUND("barney/ba_attack1.wav" );
|
||||
PRECACHE_SOUND("barney/ba_attack2.wav" );
|
||||
|
||||
PRECACHE_SOUND("barney/ba_pain1.wav");
|
||||
PRECACHE_SOUND("barney/ba_pain2.wav");
|
||||
PRECACHE_SOUND("barney/ba_pain3.wav");
|
||||
|
||||
PRECACHE_SOUND("barney/ba_die1.wav");
|
||||
PRECACHE_SOUND("barney/ba_die2.wav");
|
||||
PRECACHE_SOUND("barney/ba_die3.wav");
|
||||
|
||||
// every new barney must call this, otherwise
|
||||
// when a level is loaded, nobody will talk (time is reset to 0)
|
||||
TalkInit();
|
||||
CTalkMonster::Precache();
|
||||
}
|
||||
|
||||
// Init talk data
|
||||
void CBarney :: TalkInit()
|
||||
{
|
||||
|
||||
CTalkMonster::TalkInit();
|
||||
|
||||
// barney speech group names (group names are in sentences.txt)
|
||||
|
||||
if (!m_iszSpeakAs)
|
||||
{
|
||||
m_szGrp[TLK_ANSWER] = "BA_ANSWER";
|
||||
m_szGrp[TLK_QUESTION] = "BA_QUESTION";
|
||||
m_szGrp[TLK_IDLE] = "BA_IDLE";
|
||||
m_szGrp[TLK_STARE] = "BA_STARE";
|
||||
if (pev->spawnflags & SF_MONSTER_PREDISASTER) //LRC
|
||||
m_szGrp[TLK_USE] = "BA_PFOLLOW";
|
||||
else
|
||||
m_szGrp[TLK_USE] = "BA_OK";
|
||||
if (pev->spawnflags & SF_MONSTER_PREDISASTER)
|
||||
m_szGrp[TLK_UNUSE] = "BA_PWAIT";
|
||||
else
|
||||
m_szGrp[TLK_UNUSE] = "BA_WAIT";
|
||||
if (pev->spawnflags & SF_MONSTER_PREDISASTER)
|
||||
m_szGrp[TLK_DECLINE] = "BA_POK";
|
||||
else
|
||||
m_szGrp[TLK_DECLINE] = "BA_NOTOK";
|
||||
m_szGrp[TLK_STOP] = "BA_STOP";
|
||||
|
||||
m_szGrp[TLK_NOSHOOT] = "BA_SCARED";
|
||||
m_szGrp[TLK_HELLO] = "BA_HELLO";
|
||||
|
||||
m_szGrp[TLK_PLHURT1] = "!BA_CUREA";
|
||||
m_szGrp[TLK_PLHURT2] = "!BA_CUREB";
|
||||
m_szGrp[TLK_PLHURT3] = "!BA_CUREC";
|
||||
|
||||
m_szGrp[TLK_PHELLO] = NULL; //"BA_PHELLO"; // UNDONE
|
||||
m_szGrp[TLK_PIDLE] = NULL; //"BA_PIDLE"; // UNDONE
|
||||
m_szGrp[TLK_PQUESTION] = "BA_PQUEST"; // UNDONE
|
||||
|
||||
m_szGrp[TLK_SMELL] = "BA_SMELL";
|
||||
|
||||
m_szGrp[TLK_WOUND] = "BA_WOUND";
|
||||
m_szGrp[TLK_MORTAL] = "BA_MORTAL";
|
||||
}
|
||||
|
||||
// get voice for head - just one barney voice for now
|
||||
m_voicePitch = 100;
|
||||
}
|
||||
|
||||
int CBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType)
|
||||
{
|
||||
// make sure friends talk about it if player hurts talkmonsters...
|
||||
int ret = CTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
|
||||
if ( !IsAlive() || pev->deadflag == DEAD_DYING )
|
||||
return ret;
|
||||
|
||||
// LRC - if my reaction to the player has been overridden, don't do this stuff
|
||||
if (m_iPlayerReact) return ret;
|
||||
|
||||
if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) )
|
||||
{
|
||||
m_flPlayerDamage += flDamage;
|
||||
|
||||
// This is a heurstic to determine if the player intended to harm me
|
||||
// If I have an enemy, we can't establish intent (may just be crossfire)
|
||||
if( m_hEnemy == NULL )
|
||||
{
|
||||
// If the player was facing directly at me, or I'm already suspicious, get mad
|
||||
if(( m_afMemory & bits_MEMORY_SUSPICIOUS) || UTIL_IsFacing( pevAttacker, pev->origin ))
|
||||
{
|
||||
// Alright, now I'm pissed!
|
||||
if (m_iszSpeakAs)
|
||||
{
|
||||
char szBuf[32];
|
||||
strcpy(szBuf,STRING(m_iszSpeakAs));
|
||||
strcat(szBuf,"_MAD");
|
||||
PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySentence( "BA_MAD", 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
|
||||
Remember( bits_MEMORY_PROVOKED );
|
||||
StopFollowing( TRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hey, be careful with that
|
||||
if (m_iszSpeakAs)
|
||||
{
|
||||
char szBuf[32];
|
||||
strcpy(szBuf,STRING(m_iszSpeakAs));
|
||||
strcat(szBuf,"_SHOT");
|
||||
PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
Remember( bits_MEMORY_SUSPICIOUS );
|
||||
}
|
||||
}
|
||||
else if ( !(m_hEnemy->IsPlayer()) && pev->deadflag == DEAD_NO )
|
||||
{
|
||||
if (m_iszSpeakAs)
|
||||
{
|
||||
char szBuf[32];
|
||||
strcpy(szBuf,STRING(m_iszSpeakAs));
|
||||
strcat(szBuf,"_SHOT");
|
||||
PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySentence( "BA_SHOT", 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// PainSound
|
||||
//=========================================================
|
||||
void CBarney :: PainSound ( void )
|
||||
{
|
||||
if (gpGlobals->time < m_painTime)
|
||||
return;
|
||||
|
||||
m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75);
|
||||
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CBarney :: DeathSound ( void )
|
||||
{
|
||||
switch (RANDOM_LONG(0,2))
|
||||
{
|
||||
case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CBarney::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
switch( ptr->iHitgroup)
|
||||
{
|
||||
case HITGROUP_CHEST:
|
||||
case HITGROUP_STOMACH:
|
||||
if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST))
|
||||
{
|
||||
flDamage = flDamage / 2;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB))
|
||||
{
|
||||
flDamage -= 20;
|
||||
if (flDamage <= 0)
|
||||
{
|
||||
UTIL_Ricochet( ptr->vecEndPos, 1.0 );
|
||||
flDamage = 0.01;
|
||||
}
|
||||
}
|
||||
// always a head shot
|
||||
ptr->iHitgroup = HITGROUP_HEAD;
|
||||
break;
|
||||
}
|
||||
|
||||
CTalkMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
void CBarney::Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
if ( pev->body < m_iBaseBody + BARNEY_BODY_GUNGONE && !(pev->spawnflags & SF_MONSTER_NO_WPN_DROP))
|
||||
{ // drop the gun!
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
|
||||
pev->body = m_iBaseBody + BARNEY_BODY_GUNGONE;
|
||||
|
||||
GetAttachment( 0, vecGunPos, vecGunAngles );
|
||||
|
||||
CBaseEntity *pGun;
|
||||
pGun = DropItem( "weapon_glock", vecGunPos, vecGunAngles );
|
||||
}
|
||||
|
||||
SetUse( NULL );
|
||||
CTalkMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
Schedule_t* CBarney :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
Schedule_t *psched;
|
||||
|
||||
switch( Type )
|
||||
{
|
||||
case SCHED_ARM_WEAPON:
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
// face enemy, then draw.
|
||||
return slBarneyEnemyDraw;
|
||||
}
|
||||
break;
|
||||
|
||||
// Hook these to make a looping schedule
|
||||
case SCHED_TARGET_FACE:
|
||||
// call base class default so that barney will talk
|
||||
// when 'used'
|
||||
psched = CTalkMonster::GetScheduleOfType(Type);
|
||||
|
||||
if (psched == slIdleStand)
|
||||
return slBaFaceTarget; // override this for different target face behavior
|
||||
else
|
||||
return psched;
|
||||
|
||||
case SCHED_TARGET_CHASE:
|
||||
return slBaFollow;
|
||||
|
||||
case SCHED_IDLE_STAND:
|
||||
// call base class default so that scientist will talk
|
||||
// when standing during idle
|
||||
psched = CTalkMonster::GetScheduleOfType(Type);
|
||||
|
||||
if (psched == slIdleStand)
|
||||
{
|
||||
// just look straight ahead.
|
||||
return slIdleBaStand;
|
||||
}
|
||||
else
|
||||
return psched;
|
||||
}
|
||||
|
||||
return CTalkMonster::GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetSchedule - Decides which type of schedule best suits
|
||||
// the monster's current state and conditions. Then calls
|
||||
// monster's member function to get a pointer to a schedule
|
||||
// of the proper type.
|
||||
//=========================================================
|
||||
Schedule_t *CBarney :: GetSchedule ( void )
|
||||
{
|
||||
if ( HasConditions( bits_COND_HEAR_SOUND ) )
|
||||
{
|
||||
CSound *pSound;
|
||||
pSound = PBestSound();
|
||||
|
||||
ASSERT( pSound != NULL );
|
||||
if ( pSound && (pSound->m_iType & bits_SOUND_DANGER) )
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_BEST_SOUND );
|
||||
}
|
||||
if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() )
|
||||
{
|
||||
// Hey, be careful with that
|
||||
if (m_iszSpeakAs)
|
||||
{
|
||||
char szBuf[32];
|
||||
strcpy(szBuf,STRING(m_iszSpeakAs));
|
||||
strcat(szBuf,"_KILL");
|
||||
PlaySentence( szBuf, 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM );
|
||||
}
|
||||
}
|
||||
|
||||
switch( m_MonsterState )
|
||||
{
|
||||
case MONSTERSTATE_COMBAT:
|
||||
{
|
||||
// dead enemy
|
||||
if ( HasConditions( bits_COND_ENEMY_DEAD ) )
|
||||
{
|
||||
// call base class, all code to handle dead enemies is centralized there.
|
||||
return CBaseMonster :: GetSchedule();
|
||||
}
|
||||
|
||||
// always act surprized with a new enemy
|
||||
if ( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE) )
|
||||
return GetScheduleOfType( SCHED_SMALL_FLINCH );
|
||||
|
||||
// wait for one schedule to draw gun
|
||||
if (!m_fGunDrawn )
|
||||
return GetScheduleOfType( SCHED_ARM_WEAPON );
|
||||
|
||||
if ( HasConditions( bits_COND_HEAVY_DAMAGE ) )
|
||||
return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY );
|
||||
}
|
||||
break;
|
||||
|
||||
case MONSTERSTATE_ALERT:
|
||||
case MONSTERSTATE_IDLE:
|
||||
if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE))
|
||||
{
|
||||
// flinch if hurt
|
||||
return GetScheduleOfType( SCHED_SMALL_FLINCH );
|
||||
}
|
||||
|
||||
if ( m_hEnemy == NULL && IsFollowing() )
|
||||
{
|
||||
if ( !m_hTargetEnt->IsAlive() )
|
||||
{
|
||||
// UNDONE: Comment about the recently dead player here?
|
||||
StopFollowing( FALSE );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( HasConditions( bits_COND_CLIENT_PUSH ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW );
|
||||
}
|
||||
return GetScheduleOfType( SCHED_TARGET_FACE );
|
||||
}
|
||||
}
|
||||
|
||||
if ( HasConditions( bits_COND_CLIENT_PUSH ) )
|
||||
{
|
||||
return GetScheduleOfType( SCHED_MOVE_AWAY );
|
||||
}
|
||||
|
||||
// try to say something about smells
|
||||
TrySmellTalk();
|
||||
break;
|
||||
}
|
||||
|
||||
return CTalkMonster::GetSchedule();
|
||||
}
|
||||
|
||||
MONSTERSTATE CBarney :: GetIdealState ( void )
|
||||
{
|
||||
return CTalkMonster::GetIdealState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CBarney::DeclineFollowing( void )
|
||||
{
|
||||
PlaySentence( m_szGrp[TLK_DECLINE], 2, VOL_NORM, ATTN_NORM ); //LRC
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=========================================================
|
||||
// DEAD BARNEY PROP
|
||||
//
|
||||
// Designer selects a pose in worldcraft, 0 through num_poses-1
|
||||
// this value is added to what is selected as the 'first dead pose'
|
||||
// among the monster's normal animations. All dead poses must
|
||||
// appear sequentially in the model file. Be sure and set
|
||||
// the m_iFirstPose properly!
|
||||
//
|
||||
//=========================================================
|
||||
class CDeadBarney : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
int Classify ( void ) { return CLASS_PLAYER_ALLY; }
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
int m_iPose;// which sequence to display -- temporary, don't need to save
|
||||
static char *m_szPoses[3];
|
||||
};
|
||||
|
||||
char *CDeadBarney::m_szPoses[] = { "lying_on_back", "lying_on_side", "lying_on_stomach" };
|
||||
|
||||
void CDeadBarney::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "pose"))
|
||||
{
|
||||
m_iPose = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_barney_dead, CDeadBarney );
|
||||
|
||||
//=========================================================
|
||||
// ********** DeadBarney SPAWN **********
|
||||
//=========================================================
|
||||
void CDeadBarney :: Spawn( )
|
||||
{
|
||||
PRECACHE_MODEL("models/barney.mdl");
|
||||
SET_MODEL(ENT(pev), "models/barney.mdl");
|
||||
|
||||
pev->effects = 0;
|
||||
pev->yaw_speed = 8;
|
||||
pev->sequence = 0;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
|
||||
pev->sequence = LookupSequence( m_szPoses[m_iPose] );
|
||||
if( pev->sequence == -1 )
|
||||
{
|
||||
ALERT ( at_console, "Dead barney with bad pose\n" );
|
||||
}
|
||||
// Corpses have less health
|
||||
pev->health = DEAD_BARNEY_HEALTH;
|
||||
|
||||
MonsterInitDead();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
//=======================================================================
|
||||
// Copyright (C) XashXT Group 2006
|
||||
//=======================================================================
|
||||
|
||||
#ifndef BASEANIMATING_H
|
||||
#define BASEANIMATING_H
|
||||
|
||||
class CBaseAnimating : public CBaseLogic
|
||||
{
|
||||
public:
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
// Basic Monster Animation functions
|
||||
float StudioFrameAdvance( float flInterval = 0.0 );
|
||||
int GetSequenceFlags( void );
|
||||
int LookupActivity ( int activity );
|
||||
int LookupActivityHeaviest ( int activity );
|
||||
int LookupSequence ( const char *label );
|
||||
float SequenceDuration( int iSequence );
|
||||
void ResetSequenceInfo ( );
|
||||
void DispatchAnimEvents ( float flFutureInterval = 0.1 );
|
||||
virtual CBaseAnimating* GetBaseAnimating() { return this; }
|
||||
virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; };
|
||||
float SetBoneController ( int iController, float flValue );
|
||||
void InitBoneControllers ( void );
|
||||
float SetBlending ( int iBlender, float flValue );
|
||||
void GetBonePosition ( int iBone, Vector &origin, Vector &angles );
|
||||
void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 );
|
||||
int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir );
|
||||
BOOL GetAttachment ( int iAttachment, Vector &origin, Vector &angles );
|
||||
void SetBodygroup( int iGroup, int iValue );
|
||||
int GetBodygroup( int iGroup );
|
||||
int GetBoneCount( void );
|
||||
void SetBones( float (*data)[3], int datasize );
|
||||
|
||||
int ExtractBbox( int sequence, float *mins, float *maxs );
|
||||
void SetSequenceBox( void );
|
||||
|
||||
// animation needs
|
||||
float m_flFrameRate; // computed FPS for current sequence
|
||||
float m_flGroundSpeed; // computed linear movement rate for current sequence
|
||||
float m_flLastEventCheck; // last time the event list was checked
|
||||
BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry
|
||||
BOOL m_fSequenceLoops; // true if the sequence loops
|
||||
};
|
||||
|
||||
#endif //BASEANIMATING_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,352 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
|
||||
#ifndef BASEMONSTER_H
|
||||
#define BASEMONSTER_H
|
||||
|
||||
#define ROUTE_SIZE 8 // how many waypoints a monster can store at one time
|
||||
#define MAX_OLD_ENEMIES 4 // how many old enemies to remember
|
||||
|
||||
//
|
||||
// generic Monster
|
||||
//
|
||||
class CBaseMonster : public CBaseAnimating
|
||||
{
|
||||
private:
|
||||
int m_afConditions;
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
SCRIPT_PLAYING = 0, // Playing the sequence
|
||||
SCRIPT_WAIT, // Waiting on everyone in the script to be ready
|
||||
SCRIPT_CLEANUP, // Cancelling the script / cleaning up
|
||||
SCRIPT_WALK_TO_MARK,
|
||||
SCRIPT_RUN_TO_MARK,
|
||||
} SCRIPTSTATE;
|
||||
|
||||
|
||||
|
||||
// these fields have been added in the process of reworking the state machine. (sjb)
|
||||
EHANDLE m_hEnemy; // the entity that the monster is fighting.
|
||||
EHANDLE m_hTargetEnt; // the entity that the monster is trying to reach
|
||||
EHANDLE m_hOldEnemy[ MAX_OLD_ENEMIES ];
|
||||
Vector m_vecOldEnemy[ MAX_OLD_ENEMIES ];
|
||||
|
||||
float m_flFieldOfView;// width of monster's field of view ( dot product )
|
||||
float m_flWaitFinished;// if we're told to wait, this is the time that the wait will be over.
|
||||
float m_flMoveWaitFinished;
|
||||
|
||||
Activity m_Activity;// what the monster is doing (animation)
|
||||
Activity m_IdealActivity;// monster should switch to this activity
|
||||
|
||||
int m_LastHitGroup; // the last body region that took damage
|
||||
|
||||
MONSTERSTATE m_MonsterState;// monster's current state
|
||||
MONSTERSTATE m_IdealMonsterState;// monster should change to this state
|
||||
|
||||
int m_iTaskStatus;
|
||||
Schedule_t *m_pSchedule;
|
||||
int m_iScheduleIndex;
|
||||
|
||||
WayPoint_t m_Route[ ROUTE_SIZE ]; // Positions of movement
|
||||
int m_movementGoal; // Goal that defines route
|
||||
int m_iRouteIndex; // index into m_Route[]
|
||||
float m_moveWaitTime; // How long I should wait for something to move
|
||||
|
||||
Vector m_vecMoveGoal; // kept around for node graph moves, so we know our ultimate goal
|
||||
Activity m_movementActivity; // When moving, set this activity
|
||||
|
||||
int m_iAudibleList; // first index of a linked list of sounds that the monster can hear.
|
||||
int m_afSoundTypes;
|
||||
|
||||
Vector m_vecLastPosition;// monster sometimes wants to return to where it started after an operation.
|
||||
|
||||
int m_iHintNode; // this is the hint node that the monster is moving towards or performing active idle on.
|
||||
|
||||
int m_afMemory;
|
||||
|
||||
int m_iMaxHealth;// keeps track of monster's maximum health value (for re-healing, etc)
|
||||
|
||||
Vector m_vecEnemyLKP;// last known position of enemy. (enemy's origin)
|
||||
|
||||
int m_cAmmoLoaded; // how much ammo is in the weapon (used to trigger reload anim sequences)
|
||||
|
||||
int m_afCapability;// tells us what a monster can/can't do.
|
||||
|
||||
float m_flNextAttack; // cannot attack again until this time
|
||||
|
||||
int m_bitsDamageType; // what types of damage has monster (player) taken
|
||||
BYTE m_rgbTimeBasedDamage[CDMG_TIMEBASED];
|
||||
|
||||
int m_lastDamageAmount;// how much damage did monster (player) last take
|
||||
// time based damage counters, decr. 1 per 2 seconds
|
||||
int m_bloodColor; // color of blood particless
|
||||
|
||||
int m_failSchedule; // Schedule type to choose if current schedule fails
|
||||
|
||||
float m_flHungryTime;// set this is a future time to stop the monster from eating for a while.
|
||||
|
||||
float m_flDistTooFar; // if enemy farther away than this, bits_COND_ENEMY_TOOFAR set in CheckEnemy
|
||||
float m_flDistLook; // distance monster sees (Default 2048)
|
||||
|
||||
int m_iTriggerCondition;// for scripted AI, this is the condition that will cause the activation of the monster's TriggerTarget
|
||||
string_t m_iszTriggerTarget;// name of target that should be fired.
|
||||
|
||||
Vector m_HackedGunPos; // HACK until we can query end of gun
|
||||
|
||||
// Scripted sequence Info
|
||||
SCRIPTSTATE m_scriptState; // internal cinematic state
|
||||
CCineMonster *m_pCine;
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
virtual STATE GetState( void ) { return (pev->deadflag == DEAD_DEAD)?STATE_OFF:STATE_ON; };
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
// monster use function
|
||||
void EXPORT MonsterUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
void EXPORT CorpseUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
// overrideable Monster member functions
|
||||
|
||||
// LRC- to allow level-designers to change monster allegiances
|
||||
int m_iClass;
|
||||
int m_iPlayerReact;
|
||||
virtual int Classify( void ) { return m_iClass?m_iClass:CLASS_NONE; }
|
||||
|
||||
virtual int BloodColor( void ) { return m_bloodColor; }
|
||||
|
||||
virtual CBaseMonster *MyMonsterPointer( void ) { return this; }
|
||||
virtual void Look ( int iDistance );// basic sight function for monsters
|
||||
virtual void RunAI ( void );// core ai function!
|
||||
void Listen ( void );
|
||||
|
||||
virtual BOOL IsAlive( void ) { return (pev->deadflag != DEAD_DEAD); }
|
||||
|
||||
// Basic Monster AI functions
|
||||
virtual float ChangeYaw ( int speed );
|
||||
float VecToYaw( Vector vecDir );
|
||||
float FlYawDiff ( void );
|
||||
|
||||
float DamageForce( float damage );
|
||||
|
||||
// stuff written for new state machine
|
||||
virtual void MonsterThink( void );
|
||||
void EXPORT CallMonsterThink( void ) { this->MonsterThink(); }
|
||||
virtual int IRelationship ( CBaseEntity *pTarget );
|
||||
virtual void MonsterInit ( void );
|
||||
virtual void MonsterInitDead( void ); // Call after animation/pose is set up
|
||||
virtual void BecomeDead( void );
|
||||
void EXPORT CorpseFallThink( void );
|
||||
|
||||
void EXPORT MonsterInitThink ( void );
|
||||
virtual void StartMonster ( void );
|
||||
virtual CBaseEntity* BestVisibleEnemy ( void );// finds best visible enemy for attack
|
||||
virtual BOOL FInViewCone ( CBaseEntity *pEntity );// see if pEntity is in monster's view cone
|
||||
virtual BOOL FInViewCone ( Vector *pOrigin );// see if given location is in monster's view cone
|
||||
virtual void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
|
||||
virtual int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space
|
||||
virtual void Move( float flInterval = 0.1 );
|
||||
virtual void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
virtual BOOL ShouldAdvanceRoute( float flWaypointDist );
|
||||
|
||||
virtual Activity GetStoppedActivity( void ) { return ACT_IDLE; }
|
||||
virtual void Stop( void ) { m_IdealActivity = GetStoppedActivity(); }
|
||||
|
||||
// This will stop animation until you call ResetSequenceInfo() at some point in the future
|
||||
inline void StopAnimation( void ) { pev->framerate = 0; }
|
||||
|
||||
// these functions will survey conditions and set appropriate conditions bits for attack types.
|
||||
virtual BOOL CheckRangeAttack1( float flDot, float flDist );
|
||||
virtual BOOL CheckRangeAttack2( float flDot, float flDist );
|
||||
virtual BOOL CheckMeleeAttack1( float flDot, float flDist );
|
||||
virtual BOOL CheckMeleeAttack2( float flDot, float flDist );
|
||||
|
||||
BOOL FHaveSchedule( void );
|
||||
BOOL FScheduleValid ( void );
|
||||
void ClearSchedule( void );
|
||||
BOOL FScheduleDone ( void );
|
||||
void ChangeSchedule ( Schedule_t *pNewSchedule );
|
||||
void NextScheduledTask ( void );
|
||||
Schedule_t *ScheduleInList( const char *pName, Schedule_t **pList, int listCount );
|
||||
|
||||
virtual Schedule_t *ScheduleFromName( const char *pName );
|
||||
static Schedule_t *m_scheduleList[];
|
||||
|
||||
void MaintainSchedule ( void );
|
||||
virtual void StartTask ( Task_t *pTask );
|
||||
virtual void RunTask ( Task_t *pTask );
|
||||
virtual Schedule_t *GetScheduleOfType( int Type );
|
||||
virtual Schedule_t *GetSchedule( void );
|
||||
virtual void ScheduleChange( void ) {}
|
||||
// virtual int CanPlaySequence( void ) { return ((m_pCine == NULL) && (m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE)); }
|
||||
virtual int CanPlaySequence( int interruptFlags );
|
||||
// virtual int CanPlaySequence( BOOL fDisregardState, int interruptLevel );
|
||||
virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); }
|
||||
virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation );
|
||||
virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
|
||||
|
||||
virtual void SentenceStop( void );
|
||||
|
||||
Task_t *GetTask ( void );
|
||||
virtual MONSTERSTATE GetIdealState ( void );
|
||||
virtual void SetActivity ( Activity NewActivity );
|
||||
void SetSequenceByName ( char *szSequence );
|
||||
void SetState ( MONSTERSTATE State );
|
||||
virtual void ReportAIState( void );
|
||||
|
||||
void CheckAttacks ( CBaseEntity *pTarget, float flDist );
|
||||
virtual int CheckEnemy ( CBaseEntity *pEnemy );
|
||||
void PushEnemy( CBaseEntity *pEnemy, Vector &vecLastKnownPos );
|
||||
BOOL PopEnemy( void );
|
||||
|
||||
BOOL FGetNodeRoute ( Vector vecDest );
|
||||
|
||||
inline void TaskComplete( void ) { if ( !HasConditions(bits_COND_TASK_FAILED) ) m_iTaskStatus = TASKSTATUS_COMPLETE; }
|
||||
void MovementComplete( void );
|
||||
inline void TaskFail( void ) { SetConditions(bits_COND_TASK_FAILED); }
|
||||
inline void TaskBegin( void ) { m_iTaskStatus = TASKSTATUS_RUNNING; }
|
||||
int TaskIsRunning( void );
|
||||
inline int TaskIsComplete( void ) { return (m_iTaskStatus == TASKSTATUS_COMPLETE); }
|
||||
inline int MovementIsComplete( void ) { return (m_movementGoal == MOVEGOAL_NONE); }
|
||||
|
||||
int IScheduleFlags ( void );
|
||||
BOOL FRefreshRoute( void );
|
||||
BOOL FRouteClear ( void );
|
||||
void RouteSimplify( CBaseEntity *pTargetEnt );
|
||||
void AdvanceRoute ( float distance );
|
||||
virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex );
|
||||
void MakeIdealYaw( Vector vecTarget );
|
||||
virtual void SetYawSpeed ( void ) { return; };// allows different yaw_speeds for each activity
|
||||
BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, CBaseEntity *pTarget );
|
||||
virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
||||
int RouteClassify( int iMoveFlag );
|
||||
void InsertWaypoint ( Vector vecLocation, int afMoveFlags );
|
||||
|
||||
BOOL FindLateralCover ( const Vector &vecThreat, const Vector &vecViewOffset );
|
||||
virtual BOOL FindCover ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist );
|
||||
virtual BOOL FValidateCover ( const Vector &vecCoverLocation ) { return TRUE; };
|
||||
virtual float CoverRadius( void ) { return 784; } // Default cover radius
|
||||
|
||||
virtual BOOL FCanCheckAttacks ( void );
|
||||
virtual void CheckAmmo( void ) { return; };
|
||||
virtual int IgnoreConditions ( void );
|
||||
|
||||
inline void SetConditions( int iConditions ) { m_afConditions |= iConditions; }
|
||||
inline void ClearConditions( int iConditions ) { m_afConditions &= ~iConditions; }
|
||||
inline BOOL HasConditions( int iConditions ) { if ( m_afConditions & iConditions ) return TRUE; return FALSE; }
|
||||
inline BOOL HasAllConditions( int iConditions ) { if ( (m_afConditions & iConditions) == iConditions ) return TRUE; return FALSE; }
|
||||
|
||||
virtual BOOL FValidateHintType( short sHint );
|
||||
int FindHintNode ( void );
|
||||
virtual BOOL FCanActiveIdle ( void );
|
||||
void SetTurnActivity ( void );
|
||||
float FLSoundVolume ( CSound *pSound );
|
||||
|
||||
BOOL MoveToNode( Activity movementAct, float waitTime, const Vector &goal );
|
||||
BOOL MoveToTarget( Activity movementAct, float waitTime );
|
||||
BOOL MoveToLocation( Activity movementAct, float waitTime, const Vector &goal );
|
||||
BOOL MoveToEnemy( Activity movementAct, float waitTime );
|
||||
|
||||
// Returns the time when the door will be open
|
||||
float OpenDoorAndWait( entvars_t *pevDoor );
|
||||
|
||||
virtual int ISoundMask( void );
|
||||
virtual CSound* PBestSound ( void );
|
||||
virtual CSound* PBestScent ( void );
|
||||
virtual float HearingSensitivity( void ) { return 1.0; };
|
||||
|
||||
BOOL FBecomeProne ( void );
|
||||
virtual void BarnacleVictimBitten( entvars_t *pevBarnacle );
|
||||
virtual void BarnacleVictimReleased( void );
|
||||
|
||||
void SetEyePosition ( void );
|
||||
|
||||
BOOL FShouldEat( void );// see if a monster is 'hungry'
|
||||
void Eat ( float flFullDuration );// make the monster 'full' for a while.
|
||||
|
||||
CBaseEntity *CheckTraceHullAttack( float flDist, int iDamage, int iDmgType );
|
||||
BOOL FacingIdeal( void );
|
||||
|
||||
BOOL FCheckAITrigger( void );// checks and, if necessary, fires the monster's trigger target.
|
||||
BOOL NoFriendlyFire( void );
|
||||
|
||||
BOOL BBoxFlat( void );
|
||||
|
||||
// PrescheduleThink
|
||||
virtual void PrescheduleThink( void ) { return; };
|
||||
|
||||
BOOL GetEnemy ( void );
|
||||
void MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
|
||||
// combat functions
|
||||
float UpdateTarget ( entvars_t *pevTarget );
|
||||
virtual Activity GetDeathActivity ( void );
|
||||
Activity GetSmallFlinchActivity( void );
|
||||
virtual void Killed( entvars_t *pevAttacker, int iGib );
|
||||
virtual void GibMonster( void );
|
||||
BOOL ShouldGibMonster( int iGib );
|
||||
void CallGibMonster( void );
|
||||
virtual int HasCustomGibs( void ) { return FALSE; } //LRC
|
||||
virtual BOOL HasHumanGibs( void );
|
||||
virtual BOOL HasAlienGibs( void );
|
||||
|
||||
Vector ShootAtEnemy( const Vector &shootOrigin );
|
||||
virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ) * 0.75 + EyePosition() * 0.25; }; // position to shoot at
|
||||
|
||||
virtual Vector GetGunPosition( void );
|
||||
|
||||
virtual int TakeHealth( float flHealth, int bitsDamageType );
|
||||
virtual int TakeArmor( float flArmor, int suit = 0 );
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
int DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
void RadiusDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType );
|
||||
void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType );
|
||||
virtual int IsMoving( void ) { return m_movementGoal != MOVEGOAL_NONE; }
|
||||
|
||||
void RouteClear( void );
|
||||
void RouteNew( void );
|
||||
|
||||
virtual void DeathSound ( void ) { return; };
|
||||
virtual void AlertSound ( void ) { return; };
|
||||
virtual void IdleSound ( void ) { return; };
|
||||
virtual void PainSound ( void ) { return; };
|
||||
|
||||
virtual void StopFollowing( BOOL clearSchedule ) {}
|
||||
|
||||
inline void Remember( int iMemory ) { m_afMemory |= iMemory; }
|
||||
inline void Forget( int iMemory ) { m_afMemory &= ~iMemory; }
|
||||
inline BOOL HasMemory( int iMemory ) { if ( m_afMemory & iMemory ) return TRUE; return FALSE; }
|
||||
inline BOOL HasAllMemories( int iMemory ) { if ( (m_afMemory & iMemory) == iMemory ) return TRUE; return FALSE; }
|
||||
|
||||
BOOL ExitScriptedSequence( );
|
||||
BOOL CineCleanup( );
|
||||
|
||||
void StartPatrol( CBaseEntity *path );
|
||||
|
||||
CBaseEntity* DropItem ( char *pszItemName, const Vector &vecPos, const Vector &vecAng );// drop an item.
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // BASEMONSTER_H
|
|
@ -0,0 +1,261 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
/*
|
||||
|
||||
===== combat.cpp ========================================================
|
||||
|
||||
functions dealing with damage infliction & death
|
||||
|
||||
*/
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "client.h"
|
||||
#include "monsters.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
#include "animation.h"
|
||||
#include "baseweapon.h"
|
||||
#include "basebrush.h"
|
||||
#include "defaults.h"
|
||||
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
|
||||
extern Vector VecBModelOrigin( entvars_t* pevBModel );
|
||||
|
||||
MULTIDAMAGE gMultiDamage;
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
MULTI-DAMAGE
|
||||
|
||||
Collects multiple small damages into a single damage
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//
|
||||
// ClearMultiDamage - resets the global multi damage accumulator
|
||||
//
|
||||
void ClearMultiDamage(void)
|
||||
{
|
||||
gMultiDamage.pEntity = NULL;
|
||||
gMultiDamage.amount = 0;
|
||||
gMultiDamage.type = 0;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity
|
||||
//
|
||||
// GLOBALS USED:
|
||||
// gMultiDamage
|
||||
|
||||
void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker )
|
||||
{
|
||||
Vector vecSpot1;//where blood comes from
|
||||
Vector vecDir;//direction blood should go
|
||||
TraceResult tr;
|
||||
|
||||
if ( !gMultiDamage.pEntity )
|
||||
return;
|
||||
|
||||
gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
|
||||
}
|
||||
|
||||
|
||||
// GLOBALS USED:
|
||||
// gMultiDamage
|
||||
|
||||
void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType)
|
||||
{
|
||||
if ( !pEntity )
|
||||
return;
|
||||
|
||||
gMultiDamage.type |= bitsDamageType;
|
||||
|
||||
if ( pEntity != gMultiDamage.pEntity )
|
||||
{
|
||||
ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker!
|
||||
gMultiDamage.pEntity = pEntity;
|
||||
gMultiDamage.amount = 0;
|
||||
}
|
||||
|
||||
gMultiDamage.amount += flDamage;
|
||||
}
|
||||
|
||||
void DecalGunshot( TraceResult *pTrace, int iBulletType )
|
||||
{
|
||||
// Is the entity valid
|
||||
if ( !UTIL_IsValidEntity( pTrace->pHit ) )
|
||||
return;
|
||||
|
||||
if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP )
|
||||
{
|
||||
CBaseEntity *pEntity = NULL;
|
||||
// Decal the wall with a gunshot
|
||||
if ( !FNullEnt(pTrace->pHit) )
|
||||
pEntity = CBaseEntity::Instance(pTrace->pHit);
|
||||
|
||||
switch( iBulletType )
|
||||
{
|
||||
case BULLET_9MM:
|
||||
case BULLET_MP5:
|
||||
case BULLET_BUCKSHOT:
|
||||
case BULLET_357:
|
||||
default:
|
||||
// smoke and decal
|
||||
UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
|
||||
break;
|
||||
case BULLET_12MM:
|
||||
// smoke and decal
|
||||
UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
|
||||
break;
|
||||
case BULLET_CROWBAR:
|
||||
// wall decal
|
||||
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// EjectBrass - tosses a brass shell from passed origin at passed velocity
|
||||
//
|
||||
void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype )
|
||||
{
|
||||
// FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see.
|
||||
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecOrigin );
|
||||
WRITE_BYTE( TE_MODEL);
|
||||
WRITE_COORD( vecOrigin.x);
|
||||
WRITE_COORD( vecOrigin.y);
|
||||
WRITE_COORD( vecOrigin.z);
|
||||
WRITE_COORD( vecVelocity.x);
|
||||
WRITE_COORD( vecVelocity.y);
|
||||
WRITE_COORD( vecVelocity.z);
|
||||
WRITE_ANGLE( rotation );
|
||||
WRITE_SHORT( model );
|
||||
WRITE_BYTE ( soundtype);
|
||||
WRITE_BYTE ( 25 );// 2.5 seconds
|
||||
MESSAGE_END();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SpawnBlood
|
||||
================
|
||||
*/
|
||||
void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage)
|
||||
{
|
||||
UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage );
|
||||
}
|
||||
|
||||
|
||||
int DamageDecal( CBaseEntity *pEntity, int bitsDamageType )
|
||||
{
|
||||
if ( !pEntity )
|
||||
return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4));
|
||||
|
||||
return pEntity->DamageDecal( bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range.
|
||||
//
|
||||
// only damage ents that can clearly be seen by the explosion!
|
||||
|
||||
|
||||
void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType )
|
||||
{
|
||||
CBaseEntity *pEntity = NULL;
|
||||
TraceResult tr;
|
||||
float flAdjustedDamage, falloff;
|
||||
Vector vecSpot;
|
||||
|
||||
if ( flRadius )
|
||||
falloff = flDamage / flRadius;
|
||||
else
|
||||
falloff = 1.0;
|
||||
|
||||
int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER );
|
||||
|
||||
vecSrc.z += 1;// in case grenade is lying on the ground
|
||||
|
||||
if ( !pevAttacker )
|
||||
pevAttacker = pevInflictor;
|
||||
|
||||
// iterate on all entities in the vicinity.
|
||||
while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL)
|
||||
{
|
||||
if ( pEntity->pev->takedamage != DAMAGE_NO )
|
||||
{
|
||||
// UNDONE: this should check a damage mask, not an ignore
|
||||
if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
|
||||
{// houndeyes don't hurt other houndeyes with their attack
|
||||
continue;
|
||||
}
|
||||
|
||||
// blast's don't tavel into or out of water
|
||||
if (bInWater && pEntity->pev->waterlevel == 0)
|
||||
continue;
|
||||
if (!bInWater && pEntity->pev->waterlevel == 3)
|
||||
continue;
|
||||
|
||||
vecSpot = pEntity->BodyTarget( vecSrc );
|
||||
|
||||
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr );
|
||||
|
||||
if ( tr.flFraction == 1.0 || tr.pHit == pEntity->edict() )
|
||||
{// the explosion can 'see' this entity, so hurt them!
|
||||
if (tr.fStartSolid)
|
||||
{
|
||||
// if we're stuck inside them, fixup the position and distance
|
||||
tr.vecEndPos = vecSrc;
|
||||
tr.flFraction = 0.0;
|
||||
}
|
||||
|
||||
// decrease damage for an ent that's farther from the bomb.
|
||||
flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff;
|
||||
flAdjustedDamage = flDamage - flAdjustedDamage;
|
||||
|
||||
if ( flAdjustedDamage < 0 )
|
||||
{
|
||||
flAdjustedDamage = 0;
|
||||
}
|
||||
|
||||
// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
ClearMultiDamage( );
|
||||
pEntity->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType );
|
||||
ApplyMultiDamage( pevInflictor, pevAttacker );
|
||||
}
|
||||
else
|
||||
{
|
||||
pEntity->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
//=======================================================================
|
||||
// Copyright (C) XashXT Group 2006
|
||||
//=======================================================================
|
||||
|
||||
extern void ClearMultiDamage(void);
|
||||
extern void ApplyMultiDamage(entvars_t* pevInflictor, entvars_t* pevAttacker );
|
||||
extern void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType);
|
||||
|
||||
extern void DecalGunshot( TraceResult *pTrace, int iBulletType );
|
||||
extern void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage);
|
||||
extern int DamageDecal( CBaseEntity *pEntity, int bitsDamageType );
|
||||
extern void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType );
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CBaseEntity *pEntity;
|
||||
float amount;
|
||||
int type;
|
||||
} MULTIDAMAGE;
|
||||
|
||||
extern MULTIDAMAGE gMultiDamage;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,98 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#ifndef DEFAULTAI_H
|
||||
#define DEFAULTAI_H
|
||||
|
||||
//=========================================================
|
||||
// Failed
|
||||
//=========================================================
|
||||
extern Schedule_t slFail[];
|
||||
|
||||
//=========================================================
|
||||
// Idle Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slIdleStand[];
|
||||
extern Schedule_t slIdleTrigger[];
|
||||
extern Schedule_t slIdleWalk[];
|
||||
|
||||
//=========================================================
|
||||
// Wake Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slWakeAngry[];
|
||||
|
||||
//=========================================================
|
||||
// AlertTurn Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slAlertFace[];
|
||||
|
||||
//=========================================================
|
||||
// AlertIdle Schedules
|
||||
//=========================================================
|
||||
extern Schedule_t slAlertStand[];
|
||||
|
||||
//=========================================================
|
||||
// CombatIdle Schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slCombatStand[];
|
||||
|
||||
//=========================================================
|
||||
// CombatFace Schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slCombatFace[];
|
||||
|
||||
//=========================================================
|
||||
// reload schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slReload[];
|
||||
|
||||
//=========================================================
|
||||
// Attack Schedules
|
||||
//=========================================================
|
||||
|
||||
extern Schedule_t slRangeAttack1[];
|
||||
extern Schedule_t slRangeAttack2[];
|
||||
|
||||
extern Schedule_t slTakeCoverFromBestSound[];
|
||||
|
||||
// primary melee attack
|
||||
extern Schedule_t slMeleeAttack[];
|
||||
|
||||
// Chase enemy schedule
|
||||
extern Schedule_t slChaseEnemy[];
|
||||
|
||||
//=========================================================
|
||||
// small flinch, used when a relatively minor bit of damage
|
||||
// is inflicted.
|
||||
//=========================================================
|
||||
extern Schedule_t slSmallFlinch[];
|
||||
|
||||
//=========================================================
|
||||
// Die!
|
||||
//=========================================================
|
||||
extern Schedule_t slDie[];
|
||||
|
||||
//=========================================================
|
||||
// Universal Error Schedule
|
||||
//=========================================================
|
||||
extern Schedule_t slError[];
|
||||
|
||||
//=========================================================
|
||||
// Scripted sequences
|
||||
//=========================================================
|
||||
extern Schedule_t slWalkToScript[];
|
||||
extern Schedule_t slRunToScript[];
|
||||
extern Schedule_t slWaitScript[];
|
||||
|
||||
#endif // DEFAULTAI_H
|
|
@ -0,0 +1,281 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "flyingmonster.h"
|
||||
|
||||
#define FLYING_AE_FLAP (8)
|
||||
#define FLYING_AE_FLAPSOUND (9)
|
||||
|
||||
|
||||
extern DLL_GLOBAL edict_t *g_pBodyQueueHead;
|
||||
|
||||
int CFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist )
|
||||
{
|
||||
// UNDONE: need to check more than the endpoint
|
||||
if (FBitSet( pev->flags, FL_SWIM) && (!(UTIL_PointContents( vecEnd ) & MASK_WATER )))
|
||||
{
|
||||
// ALERT(at_aiconsole, "can't swim out of water\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceHull( vecStart + Vector( 0, 0, 32 ), vecEnd + Vector( 0, 0, 32 ), dont_ignore_monsters, large_hull, edict(), &tr );
|
||||
|
||||
// ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z );
|
||||
// ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z );
|
||||
|
||||
if (pflDist)
|
||||
{
|
||||
*pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance.
|
||||
}
|
||||
|
||||
// ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction );
|
||||
if (tr.fStartSolid || tr.flFraction < 1.0)
|
||||
{
|
||||
if ( pTarget && pTarget->edict() == gpGlobals->trace_ent )
|
||||
return LOCALMOVE_VALID;
|
||||
return LOCALMOVE_INVALID;
|
||||
}
|
||||
|
||||
return LOCALMOVE_VALID;
|
||||
}
|
||||
|
||||
|
||||
BOOL CFlyingMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex )
|
||||
{
|
||||
return CBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex );
|
||||
}
|
||||
|
||||
|
||||
Activity CFlyingMonster :: GetStoppedActivity( void )
|
||||
{
|
||||
if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else
|
||||
return ACT_IDLE;
|
||||
|
||||
return ACT_HOVER;
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: Stop( void )
|
||||
{
|
||||
Activity stopped = GetStoppedActivity();
|
||||
if ( m_IdealActivity != stopped )
|
||||
{
|
||||
m_flightSpeed = 0;
|
||||
m_IdealActivity = stopped;
|
||||
}
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
m_vecTravel = g_vecZero;
|
||||
}
|
||||
|
||||
|
||||
float CFlyingMonster :: ChangeYaw( int speed )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
float diff = FlYawDiff();
|
||||
float target = 0;
|
||||
|
||||
if ( m_IdealActivity != GetStoppedActivity() )
|
||||
{
|
||||
if ( diff < -20 )
|
||||
target = 90;
|
||||
else if ( diff > 20 )
|
||||
target = -90;
|
||||
}
|
||||
pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime );
|
||||
}
|
||||
return CBaseMonster::ChangeYaw( speed );
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
CBaseMonster::Killed( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case FLYING_AE_FLAP:
|
||||
m_flightSpeed = 400;
|
||||
break;
|
||||
|
||||
case FLYING_AE_FLAPSOUND:
|
||||
if ( m_pFlapSound )
|
||||
EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM );
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster :: Move( float flInterval )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
m_flGroundSpeed = m_flightSpeed;
|
||||
CBaseMonster::Move( flInterval );
|
||||
}
|
||||
|
||||
|
||||
BOOL CFlyingMonster:: ShouldAdvanceRoute( float flWaypointDist )
|
||||
{
|
||||
// Get true 3D distance to the goal so we actually reach the correct height
|
||||
if ( m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL )
|
||||
flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length();
|
||||
|
||||
if ( flWaypointDist <= 64 + (m_flGroundSpeed * gpGlobals->frametime) )
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void CFlyingMonster::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
|
||||
{
|
||||
if ( pev->movetype == MOVETYPE_FLY )
|
||||
{
|
||||
if ( gpGlobals->time - m_stopTime > 1.0 )
|
||||
{
|
||||
if ( m_IdealActivity != m_movementActivity )
|
||||
{
|
||||
m_IdealActivity = m_movementActivity;
|
||||
m_flGroundSpeed = m_flightSpeed = 200;
|
||||
}
|
||||
}
|
||||
Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * m_momentum) ).Normalize() * (m_flGroundSpeed * flInterval));
|
||||
|
||||
if ( m_IdealActivity != m_movementActivity )
|
||||
{
|
||||
m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75 * gpGlobals->frametime );
|
||||
if ( m_flightSpeed < 100 )
|
||||
m_stopTime = gpGlobals->time;
|
||||
}
|
||||
else
|
||||
m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300 * gpGlobals->frametime );
|
||||
|
||||
if ( CheckLocalMove ( pev->origin, vecMove, pTargetEnt, NULL ) )
|
||||
{
|
||||
m_vecTravel = (vecMove - pev->origin);
|
||||
m_vecTravel = m_vecTravel.Normalize();
|
||||
UTIL_MoveToOrigin(ENT(pev), vecMove, (m_flGroundSpeed * flInterval), MOVE_STRAFE);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_IdealActivity = GetStoppedActivity();
|
||||
m_stopTime = gpGlobals->time;
|
||||
m_vecTravel = g_vecZero;
|
||||
}
|
||||
}
|
||||
else
|
||||
CBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval );
|
||||
}
|
||||
|
||||
|
||||
float CFlyingMonster::CeilingZ( const Vector &position )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
Vector minUp = position;
|
||||
Vector maxUp = position;
|
||||
maxUp.z += 4096.0;
|
||||
|
||||
UTIL_TraceLine(position, maxUp, ignore_monsters, NULL, &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
maxUp.z = tr.vecEndPos.z;
|
||||
|
||||
if( pev->flags & FL_SWIM )
|
||||
{
|
||||
return UTIL_WaterLevel( position, minUp.z, maxUp.z );
|
||||
}
|
||||
return maxUp.z;
|
||||
}
|
||||
|
||||
BOOL CFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction)
|
||||
{
|
||||
int conPosition = UTIL_PointContents( position );
|
||||
if ( ((pev->flags & FL_SWIM) == FL_SWIM) ^ (conPosition & CONTENTS_WATER))
|
||||
{
|
||||
// SWIMING & !WATER
|
||||
// or FLYING & WATER
|
||||
//
|
||||
*pFraction = 0.0;
|
||||
return TRUE; // We hit a water boundary because we are where we don't belong.
|
||||
}
|
||||
int conProbe = UTIL_PointContents(probe);
|
||||
if( conProbe == conPosition )
|
||||
{
|
||||
// The probe is either entirely inside the water (for fish) or entirely
|
||||
// outside the water (for birds).
|
||||
//
|
||||
*pFraction = 1.0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Vector ProbeUnit = (probe-position).Normalize();
|
||||
float ProbeLength = (probe-position).Length();
|
||||
float maxProbeLength = ProbeLength;
|
||||
float minProbeLength = 0;
|
||||
|
||||
float diff = maxProbeLength - minProbeLength;
|
||||
while (diff > 1.0)
|
||||
{
|
||||
float midProbeLength = minProbeLength + diff/2.0;
|
||||
Vector midProbeVec = midProbeLength * ProbeUnit;
|
||||
if( UTIL_PointContents( position + midProbeVec ) == conPosition )
|
||||
{
|
||||
minProbeLength = midProbeLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxProbeLength = midProbeLength;
|
||||
}
|
||||
diff = maxProbeLength - minProbeLength;
|
||||
}
|
||||
*pFraction = minProbeLength/ProbeLength;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
float CFlyingMonster::FloorZ( const Vector &position )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
Vector down = position;
|
||||
down.z -= 2048;
|
||||
|
||||
UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr );
|
||||
|
||||
if ( tr.flFraction != 1.0 )
|
||||
return tr.vecEndPos.z;
|
||||
|
||||
return down.z;
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster
|
||||
|
||||
#ifndef FLYINGMONSTER_H
|
||||
#define FLYINGMONSTER_H
|
||||
|
||||
class CFlyingMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, CBaseEntity *pTarget, float *pflDist );// check validity of a straight move through space
|
||||
BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, CBaseEntity *pTargetEnt, Vector *pApex );
|
||||
Activity GetStoppedActivity( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void Stop( void );
|
||||
float ChangeYaw( int speed );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );
|
||||
void Move( float flInterval = 0.1 );
|
||||
BOOL ShouldAdvanceRoute( float flWaypointDist );
|
||||
|
||||
inline void SetFlyingMomentum( float momentum ) { m_momentum = momentum; }
|
||||
inline void SetFlyingFlapSound( const char *pFlapSound ) { m_pFlapSound = pFlapSound; }
|
||||
inline void SetFlyingSpeed( float speed ) { m_flightSpeed = speed; }
|
||||
float CeilingZ( const Vector &position );
|
||||
float FloorZ( const Vector &position );
|
||||
BOOL ProbeZ( const Vector &position, const Vector &probe, float *pFraction );
|
||||
|
||||
|
||||
// UNDONE: Save/restore this stuff!!!
|
||||
protected:
|
||||
Vector m_vecTravel; // Current direction
|
||||
float m_flightSpeed; // Current flight speed (decays when not flapping or gliding)
|
||||
float m_stopTime; // Last time we stopped (to avoid switching states too soon)
|
||||
float m_momentum; // Weight for desired vs. momentum velocity
|
||||
const char *m_pFlapSound;
|
||||
};
|
||||
|
||||
|
||||
#endif //FLYINGMONSTER_H
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Generic Monster - purely for scripted sequence work.
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "animation.h"
|
||||
#include "talkmonster.h"
|
||||
#include "basebeams.h"
|
||||
|
||||
// For holograms, make them not solid so the player can walk through them
|
||||
//LRC- this seems to interfere with SF_MONSTER_CLIP
|
||||
#define SF_GENERICMONSTER_NOTSOLID 4
|
||||
#define SF_HEAD_CONTROLLER 8
|
||||
#define SF_GENERICMONSTER_INVULNERABLE 32
|
||||
#define SF_GENERICMONSTER_PLAYERMODEL 64
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
//G-Cont. This code - support for htorch model from Op4 ;)
|
||||
#define HTORCH_AE_SHOWGUN ( 17)
|
||||
#define HTORCH_AE_SHOWTORCH ( 18)
|
||||
#define HTORCH_AE_HIDETORCH ( 19)
|
||||
#define HTORCH_AE_ONGAS ( 20)
|
||||
#define HTORCH_AE_OFFGAS ( 21)
|
||||
#define GUN_DEAGLE 0
|
||||
#define GUN_TORCH 1
|
||||
#define GUN_NONE 2
|
||||
|
||||
class CGenericMonster : public CTalkMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int ISoundMask ( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
void Torch ( void );
|
||||
void MakeGas( void );
|
||||
void UpdateGas( void );
|
||||
void KillGas( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
virtual int HasCustomGibs( void ) { return m_iszGibModel; }
|
||||
|
||||
CBeam *m_pBeam;
|
||||
int m_iszGibModel;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_generic, CGenericMonster );
|
||||
|
||||
TYPEDESCRIPTION CGenericMonster::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CGenericMonster, m_iszGibModel, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CGenericMonster, CBaseMonster );
|
||||
|
||||
void CGenericMonster::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "m_bloodColor"))
|
||||
{
|
||||
m_bloodColor = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "m_iszGibModel"))
|
||||
{
|
||||
m_iszGibModel = ALLOC_STRING(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CGenericMonster :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_PLAYER_ALLY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CGenericMonster :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
default:
|
||||
ys = 90;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CGenericMonster :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
Vector vecShootDir;
|
||||
Vector vecShootOrigin;
|
||||
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case HTORCH_AE_SHOWTORCH:
|
||||
pev->body = GUN_NONE;
|
||||
pev->body = GUN_TORCH;
|
||||
break;
|
||||
|
||||
case HTORCH_AE_SHOWGUN:
|
||||
pev->body = GUN_NONE;
|
||||
pev->body = GUN_DEAGLE;
|
||||
break;
|
||||
|
||||
case HTORCH_AE_HIDETORCH:
|
||||
pev->body = GUN_NONE;
|
||||
break;
|
||||
|
||||
case HTORCH_AE_ONGAS:
|
||||
{
|
||||
int gas = 1;
|
||||
MakeGas();
|
||||
UpdateGas();
|
||||
};
|
||||
break;
|
||||
|
||||
case HTORCH_AE_OFFGAS:
|
||||
{
|
||||
int gas = 0;
|
||||
KillGas();
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - generic monster can't hear.
|
||||
//=========================================================
|
||||
int CGenericMonster :: ISoundMask ( void )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CGenericMonster :: Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
UTIL_SetModel( ENT(pev), STRING(pev->model) );
|
||||
|
||||
if ( FStrEq( STRING(pev->model), "models/player.mdl" ) || FStrEq( STRING(pev->model), "models/holo.mdl" ))
|
||||
UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX);
|
||||
else UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
//UTIL_AutoSetSize();
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
//pev->weaponmodel = MAKE_STRING("materials/weapons/pulserifle/wp_prifle.mdl");
|
||||
if (!m_bloodColor) m_bloodColor = BLOOD_COLOR_RED;
|
||||
if (!pev->health) pev->health = 8;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
|
||||
m_afCapability = bits_CAP_TURN_HEAD;
|
||||
|
||||
if ( pev->spawnflags & SF_GENERICMONSTER_NOTSOLID )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
}
|
||||
else if ( pev->spawnflags & SF_GENERICMONSTER_INVULNERABLE )
|
||||
{
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CGenericMonster :: Precache()
|
||||
{
|
||||
//PRECACHE_MODEL("materials/weapons/pulserifle/wp_prifle.mdl");
|
||||
CTalkMonster::Precache();
|
||||
TalkInit();
|
||||
UTIL_PrecacheModel((char *)STRING(pev->model) );
|
||||
if (m_iszGibModel)
|
||||
PRECACHE_MODEL( (char*)STRING(m_iszGibModel) ); //LRC
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// monster-specific schedule types
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
TASK_TORCH_CHECK_FIRE = LAST_COMMON_SCHEDULE + 1,
|
||||
TASK_GAS,
|
||||
};
|
||||
|
||||
// =========================================================
|
||||
// TORCH SUPPORT
|
||||
// =========================================================
|
||||
void CGenericMonster :: Torch ( void )
|
||||
{
|
||||
Vector vecGunPos;
|
||||
Vector vecGunAngles;
|
||||
Vector vecShootDir;
|
||||
|
||||
GetAttachment( 4, vecGunPos, vecGunAngles );
|
||||
pev->effects |= EF_MUZZLEFLASH;
|
||||
|
||||
Vector angDir = UTIL_VecToAngles( vecShootDir );
|
||||
SetBlending( 0, angDir.x );
|
||||
}
|
||||
|
||||
void CGenericMonster::UpdateGas( void ) { }
|
||||
|
||||
void CGenericMonster::MakeGas( void )
|
||||
{
|
||||
Vector posGun, angleGun;
|
||||
TraceResult tr;
|
||||
UTIL_MakeVectors( pev->angles );
|
||||
{
|
||||
KillGas();
|
||||
m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 7 );
|
||||
if ( m_pBeam )
|
||||
{
|
||||
GetAttachment( 4, posGun, angleGun );
|
||||
GetAttachment( 3, posGun, angleGun );
|
||||
|
||||
Vector vecEnd = (gpGlobals->v_forward * 5) + posGun;
|
||||
UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &tr );
|
||||
|
||||
m_pBeam->EntsInit( entindex(), entindex() );
|
||||
m_pBeam->SetColor( 24, 121, 239 );
|
||||
m_pBeam->SetBrightness( 190 );
|
||||
m_pBeam->SetScrollRate( 20 );
|
||||
m_pBeam->SetStartAttachment( 4 );
|
||||
m_pBeam->SetEndAttachment( 3 );
|
||||
m_pBeam->DamageDecal( 28 );
|
||||
m_pBeam->DoSparks( tr.vecEndPos, posGun );
|
||||
m_pBeam->SetFlags( BEAM_FSHADEIN );
|
||||
m_pBeam->RelinkBeam();
|
||||
|
||||
UTIL_Sparks( tr.vecEndPos );
|
||||
UTIL_DecalTrace(&tr, 28 + RANDOM_LONG(0,4));
|
||||
}
|
||||
}
|
||||
// m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 );
|
||||
if ( int gas = 1 )
|
||||
{
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
}
|
||||
|
||||
void CGenericMonster :: KillGas( void )
|
||||
{
|
||||
if ( m_pBeam )
|
||||
{
|
||||
UTIL_Remove( m_pBeam );
|
||||
m_pBeam = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GENERIC DEAD MONSTER, PROP
|
||||
//=========================================================
|
||||
class CDeadGenericMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify ( void ) { return CLASS_PLAYER_ALLY; }
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
virtual int HasCustomGibs( void ) { return m_iszGibModel; }
|
||||
|
||||
int m_iszGibModel;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_generic_dead, CDeadGenericMonster );
|
||||
|
||||
TYPEDESCRIPTION CDeadGenericMonster::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CDeadGenericMonster, m_iszGibModel, FIELD_STRING ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CDeadGenericMonster, CBaseMonster );
|
||||
|
||||
void CDeadGenericMonster::KeyValue( KeyValueData *pkvd )
|
||||
{
|
||||
if (FStrEq(pkvd->szKeyName, "m_bloodColor"))
|
||||
{
|
||||
m_bloodColor = atoi(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else if (FStrEq(pkvd->szKeyName, "m_iszGibModel"))
|
||||
{
|
||||
m_iszGibModel = ALLOC_STRING(pkvd->szValue);
|
||||
pkvd->fHandled = TRUE;
|
||||
}
|
||||
else
|
||||
CBaseMonster::KeyValue( pkvd );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ********** DeadGenericMonster SPAWN **********
|
||||
//=========================================================
|
||||
void CDeadGenericMonster :: Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
SET_MODEL(ENT(pev), STRING(pev->model));
|
||||
|
||||
pev->effects = 0;
|
||||
pev->yaw_speed = 8; //LRC -- what?
|
||||
pev->sequence = 0;
|
||||
|
||||
if( pev->netname )
|
||||
{
|
||||
pev->sequence = LookupSequence( STRING( pev->netname ));
|
||||
|
||||
if( pev->sequence == -1 )
|
||||
{
|
||||
ALERT ( at_console, "Invalid sequence name \"%s\" in monster_generic_dead\n", STRING(pev->netname) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->sequence = LookupActivity( pev->frags );
|
||||
// if (pev->sequence == -1)
|
||||
// {
|
||||
// ALERT ( at_error, "monster_generic_dead - specify a sequence name or choose a different death type: model \"%s\" has no available death sequences.\n", STRING(pev->model) );
|
||||
// }
|
||||
//...and if that doesn't work, forget it.
|
||||
}
|
||||
|
||||
// Corpses have less health
|
||||
pev->health = 8;
|
||||
|
||||
MonsterInitDead();
|
||||
|
||||
ResetSequenceInfo( );
|
||||
pev->frame = 255; // pose at the _end_ of its death sequence.
|
||||
}
|
||||
|
||||
void CDeadGenericMonster :: Precache()
|
||||
{
|
||||
PRECACHE_MODEL( (char*)STRING(pev->model) );
|
||||
if (m_iszGibModel)
|
||||
PRECACHE_MODEL( (char*)STRING(m_iszGibModel) ); //LRC
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// GMan - misunderstood servant of the people
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "baseweapon.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
class CGMan : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int ISoundMask ( void );
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
|
||||
void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
|
||||
|
||||
EHANDLE m_hPlayer;
|
||||
EHANDLE m_hTalkTarget;
|
||||
float m_flTalkTime;
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_gman, CGMan );
|
||||
|
||||
|
||||
TYPEDESCRIPTION CGMan::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CGMan, m_hTalkTarget, FIELD_EHANDLE ),
|
||||
DEFINE_FIELD( CGMan, m_flTalkTime, FIELD_TIME ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( CGMan, CBaseMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CGMan :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_NONE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CGMan :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
default:
|
||||
ys = 90;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CGMan :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - generic monster can't hear.
|
||||
//=========================================================
|
||||
int CGMan :: ISoundMask ( void )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CGMan :: Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL( ENT(pev), "models/gman.mdl" );
|
||||
UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = DONT_BLEED;
|
||||
pev->health = 100;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CGMan :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL( "models/gman.mdl" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
void CGMan :: StartTask( Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT:
|
||||
if (m_hPlayer == NULL)
|
||||
{
|
||||
m_hPlayer = UTIL_FindEntityByClassname( NULL, "player" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
CBaseMonster::StartTask( pTask );
|
||||
}
|
||||
|
||||
void CGMan :: RunTask( Task_t *pTask )
|
||||
{
|
||||
switch( pTask->iTask )
|
||||
{
|
||||
case TASK_WAIT:
|
||||
// look at who I'm talking to
|
||||
if (m_flTalkTime > gpGlobals->time && m_hTalkTarget != NULL)
|
||||
{
|
||||
float yaw = VecToYaw(m_hTalkTarget->pev->origin - pev->origin) - pev->angles.y;
|
||||
|
||||
if (yaw > 180) yaw -= 360;
|
||||
if (yaw < -180) yaw += 360;
|
||||
|
||||
// turn towards vector
|
||||
SetBoneController( 0, yaw );
|
||||
}
|
||||
// look at player, but only if playing a "safe" idle animation
|
||||
else if (m_hPlayer != NULL && pev->sequence == 0)
|
||||
{
|
||||
float yaw = VecToYaw(m_hPlayer->pev->origin - pev->origin) - pev->angles.y;
|
||||
|
||||
if (yaw > 180) yaw -= 360;
|
||||
if (yaw < -180) yaw += 360;
|
||||
|
||||
// turn towards vector
|
||||
SetBoneController( 0, yaw );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetBoneController( 0, 0 );
|
||||
}
|
||||
CBaseMonster::RunTask( pTask );
|
||||
break;
|
||||
default:
|
||||
SetBoneController( 0, 0 );
|
||||
CBaseMonster::RunTask( pTask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Override all damage
|
||||
//=========================================================
|
||||
int CGMan :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
pev->health = pev->max_health / 2; // always trigger the 50% damage aitrigger
|
||||
|
||||
if ( flDamage > 0 )
|
||||
{
|
||||
SetConditions(bits_COND_LIGHT_DAMAGE);
|
||||
}
|
||||
|
||||
if ( flDamage >= 20 )
|
||||
{
|
||||
SetConditions(bits_COND_HEAVY_DAMAGE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void CGMan::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
UTIL_Ricochet( ptr->vecEndPos, 1.0 );
|
||||
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
void CGMan::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener )
|
||||
{
|
||||
CBaseMonster::PlayScriptedSentence( pszSentence, duration, volume, attenuation, bConcurrent, pListener );
|
||||
|
||||
m_flTalkTime = gpGlobals->time + duration;
|
||||
m_hTalkTarget = pListener;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,562 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// headcrab.cpp - tiny, jumpy alien parasite
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "game.h"
|
||||
#include "defaults.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define HC_AE_JUMPATTACK ( 2 )
|
||||
|
||||
Task_t tlHCRangeAttack1[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, (float)0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_WAIT_RANDOM, (float)0.5 },
|
||||
};
|
||||
|
||||
Schedule_t slHCRangeAttack1[] =
|
||||
{
|
||||
{
|
||||
tlHCRangeAttack1,
|
||||
ARRAYSIZE ( tlHCRangeAttack1 ),
|
||||
bits_COND_ENEMY_OCCLUDED |
|
||||
bits_COND_NO_AMMO_LOADED,
|
||||
0,
|
||||
"HCRangeAttack1"
|
||||
},
|
||||
};
|
||||
|
||||
Task_t tlHCRangeAttack1Fast[] =
|
||||
{
|
||||
{ TASK_STOP_MOVING, (float)0 },
|
||||
{ TASK_FACE_IDEAL, (float)0 },
|
||||
{ TASK_RANGE_ATTACK1, (float)0 },
|
||||
{ TASK_SET_ACTIVITY, (float)ACT_IDLE },
|
||||
};
|
||||
|
||||
Schedule_t slHCRangeAttack1Fast[] =
|
||||
{
|
||||
{
|
||||
tlHCRangeAttack1Fast,
|
||||
ARRAYSIZE ( tlHCRangeAttack1Fast ),
|
||||
bits_COND_ENEMY_OCCLUDED |
|
||||
bits_COND_NO_AMMO_LOADED,
|
||||
0,
|
||||
"HCRAFast"
|
||||
},
|
||||
};
|
||||
|
||||
class CHeadCrab : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void RunTask ( Task_t *pTask );
|
||||
void StartTask ( Task_t *pTask );
|
||||
void SetYawSpeed ( void );
|
||||
void EXPORT LeapTouch ( CBaseEntity *pOther );
|
||||
Vector Center( void );
|
||||
Vector BodyTarget( const Vector &posSrc );
|
||||
void PainSound( void );
|
||||
void DeathSound( void );
|
||||
void IdleSound( void );
|
||||
void AlertSound( void );
|
||||
void PrescheduleThink( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
|
||||
virtual float GetDamageAmount( void ) { return HEADCRAB_DMG_BITE; }
|
||||
virtual int GetVoicePitch( void ) { return 100; }
|
||||
virtual float GetSoundVolue( void ) { return 1.0; }
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
|
||||
CUSTOM_SCHEDULES;
|
||||
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pDeathSounds[];
|
||||
static const char *pBiteSounds[];
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_headcrab, CHeadCrab );
|
||||
|
||||
DEFINE_CUSTOM_SCHEDULES( CHeadCrab )
|
||||
{
|
||||
slHCRangeAttack1,
|
||||
slHCRangeAttack1Fast,
|
||||
};
|
||||
|
||||
IMPLEMENT_CUSTOM_SCHEDULES( CHeadCrab, CBaseMonster );
|
||||
|
||||
const char *CHeadCrab::pIdleSounds[] =
|
||||
{
|
||||
"headcrab/hc_idle1.wav",
|
||||
"headcrab/hc_idle2.wav",
|
||||
"headcrab/hc_idle3.wav",
|
||||
};
|
||||
const char *CHeadCrab::pAlertSounds[] =
|
||||
{
|
||||
"headcrab/hc_alert1.wav",
|
||||
};
|
||||
const char *CHeadCrab::pPainSounds[] =
|
||||
{
|
||||
"headcrab/hc_pain1.wav",
|
||||
"headcrab/hc_pain2.wav",
|
||||
"headcrab/hc_pain3.wav",
|
||||
};
|
||||
const char *CHeadCrab::pAttackSounds[] =
|
||||
{
|
||||
"headcrab/hc_attack1.wav",
|
||||
"headcrab/hc_attack2.wav",
|
||||
"headcrab/hc_attack3.wav",
|
||||
};
|
||||
|
||||
const char *CHeadCrab::pDeathSounds[] =
|
||||
{
|
||||
"headcrab/hc_die1.wav",
|
||||
"headcrab/hc_die2.wav",
|
||||
};
|
||||
|
||||
const char *CHeadCrab::pBiteSounds[] =
|
||||
{
|
||||
"headcrab/hc_headbite.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CHeadCrab :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_PREY;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Center - returns the real center of the headcrab. The
|
||||
// bounding box is much larger than the actual creature so
|
||||
// this is needed for targeting
|
||||
//=========================================================
|
||||
Vector CHeadCrab :: Center ( void )
|
||||
{
|
||||
return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 6 );
|
||||
}
|
||||
|
||||
|
||||
Vector CHeadCrab :: BodyTarget( const Vector &posSrc )
|
||||
{
|
||||
return Center( );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CHeadCrab :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
ys = 30;
|
||||
break;
|
||||
case ACT_RUN:
|
||||
case ACT_WALK:
|
||||
ys = 20;
|
||||
break;
|
||||
case ACT_TURN_LEFT:
|
||||
case ACT_TURN_RIGHT:
|
||||
ys = 60;
|
||||
break;
|
||||
case ACT_RANGE_ATTACK1:
|
||||
ys = 30;
|
||||
break;
|
||||
default:
|
||||
ys = 30;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case HC_AE_JUMPATTACK:
|
||||
{
|
||||
ClearBits( pev->flags, FL_ONGROUND );
|
||||
|
||||
UTIL_SetOrigin (this, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground
|
||||
UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
Vector vecJumpDir;
|
||||
if (m_hEnemy != NULL)
|
||||
{
|
||||
float gravity = CVAR_GET_FLOAT( "sv_gravity" );
|
||||
if (gravity <= 1)
|
||||
gravity = 1;
|
||||
|
||||
// How fast does the headcrab need to travel to reach that height given gravity?
|
||||
float height = (m_hEnemy->pev->origin.z + m_hEnemy->pev->view_ofs.z - pev->origin.z);
|
||||
if (height < 16)
|
||||
height = 16;
|
||||
float speed = sqrt( 2 * gravity * height );
|
||||
float time = speed / gravity;
|
||||
|
||||
// Scale the sideways velocity to get there at the right time
|
||||
vecJumpDir = (m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs - pev->origin);
|
||||
vecJumpDir = vecJumpDir * ( 1.0 / time );
|
||||
|
||||
// Speed to offset gravity at the desired height
|
||||
vecJumpDir.z = speed;
|
||||
|
||||
// Don't jump too far/fast
|
||||
float distance = vecJumpDir.Length();
|
||||
|
||||
if (distance > 650)
|
||||
{
|
||||
vecJumpDir = vecJumpDir * ( 650.0 / distance );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// jump hop, don't care where
|
||||
vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350;
|
||||
}
|
||||
|
||||
int iSound = RANDOM_LONG(0,2);
|
||||
if ( iSound != 0 )
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
|
||||
pev->velocity = vecJumpDir;
|
||||
m_flNextAttack = gpGlobals->time + 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CHeadCrab :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
UTIL_SetModel( ENT( pev ), pev->model, "models/monsters/headcrab.mdl" );
|
||||
UTIL_SetSize( pev, Vector( -12, -12, 0 ), Vector( 12, 12, 24 ));
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
pev->effects = 0;
|
||||
if( pev->health == 0 ) pev->health = HEADCRAB_HEALTH;
|
||||
pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin.
|
||||
pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim?
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CHeadCrab :: Precache()
|
||||
{
|
||||
PRECACHE_SOUND_ARRAY(pIdleSounds);
|
||||
PRECACHE_SOUND_ARRAY(pAlertSounds);
|
||||
PRECACHE_SOUND_ARRAY(pPainSounds);
|
||||
PRECACHE_SOUND_ARRAY(pAttackSounds);
|
||||
PRECACHE_SOUND_ARRAY(pDeathSounds);
|
||||
PRECACHE_SOUND_ARRAY(pBiteSounds);
|
||||
|
||||
UTIL_PrecacheModel( pev->model, "models/monsters/headcrab.mdl" );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// RunTask
|
||||
//=========================================================
|
||||
void CHeadCrab :: RunTask ( Task_t *pTask )
|
||||
{
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
case TASK_RANGE_ATTACK2:
|
||||
{
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
TaskComplete();
|
||||
SetTouch( NULL );
|
||||
m_IdealActivity = ACT_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: RunTask(pTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// LeapTouch - this is the headcrab's touch function when it
|
||||
// is in the air
|
||||
//=========================================================
|
||||
void CHeadCrab :: LeapTouch ( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther->pev->takedamage )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pOther->Classify() == Classify() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't hit if back on ground
|
||||
if ( !FBitSet( pev->flags, FL_ONGROUND ) )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
|
||||
pOther->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH );
|
||||
}
|
||||
|
||||
SetTouch( NULL );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// PrescheduleThink
|
||||
//=========================================================
|
||||
void CHeadCrab :: PrescheduleThink ( void )
|
||||
{
|
||||
// make the crab coo a little bit in combat state
|
||||
if ( m_MonsterState == MONSTERSTATE_COMBAT && RANDOM_FLOAT( 0, 5 ) < 0.1 )
|
||||
{
|
||||
IdleSound();
|
||||
}
|
||||
}
|
||||
|
||||
void CHeadCrab :: StartTask ( Task_t *pTask )
|
||||
{
|
||||
m_iTaskStatus = TASKSTATUS_RUNNING;
|
||||
|
||||
switch ( pTask->iTask )
|
||||
{
|
||||
case TASK_RANGE_ATTACK1:
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
m_IdealActivity = ACT_RANGE_ATTACK1;
|
||||
SetTouch(&CHeadCrab :: LeapTouch );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CBaseMonster :: StartTask( pTask );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack1
|
||||
//=========================================================
|
||||
BOOL CHeadCrab :: CheckRangeAttack1 ( float flDot, float flDist )
|
||||
{
|
||||
if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist <= 256 && flDot >= 0.65 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckRangeAttack2
|
||||
//=========================================================
|
||||
BOOL CHeadCrab :: CheckRangeAttack2 ( float flDot, float flDist )
|
||||
{
|
||||
return FALSE;
|
||||
// BUGBUG: Why is this code here? There is no ACT_RANGE_ATTACK2 animation. I've disabled it for now.
|
||||
#if 0
|
||||
if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 256 && flDot >= 0.5 )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
// Don't take any acid damage -- BigMomma's mortar is acid
|
||||
if ( bitsDamageType & DMG_ACID )
|
||||
flDamage = 0;
|
||||
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// IdleSound
|
||||
//=========================================================
|
||||
#define CRAB_ATTN_IDLE (float)1.5
|
||||
void CHeadCrab :: IdleSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AlertSound
|
||||
//=========================================================
|
||||
void CHeadCrab :: AlertSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AlertSound
|
||||
//=========================================================
|
||||
void CHeadCrab :: PainSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// DeathSound
|
||||
//=========================================================
|
||||
void CHeadCrab :: DeathSound ( void )
|
||||
{
|
||||
EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolue(), ATTN_IDLE, 0, GetVoicePitch() );
|
||||
}
|
||||
|
||||
Schedule_t* CHeadCrab :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
switch ( Type )
|
||||
{
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
{
|
||||
return &slHCRangeAttack1[ 0 ];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CBaseMonster::GetScheduleOfType( Type );
|
||||
}
|
||||
|
||||
|
||||
class CBabyCrab : public CHeadCrab
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed ( void );
|
||||
float GetDamageAmount( void ) { return HEADCRAB_HEALTH * 0.3; }
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist );
|
||||
Schedule_t* GetScheduleOfType ( int Type );
|
||||
virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); }
|
||||
virtual float GetSoundVolue( void ) { return 0.8; }
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_babycrab, CBabyCrab );
|
||||
|
||||
void CBabyCrab :: Spawn( void )
|
||||
{
|
||||
CHeadCrab::Spawn();
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/baby_headcrab.mdl");
|
||||
pev->rendermode = kRenderTransTexture;
|
||||
pev->renderamt = 192;
|
||||
UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24));
|
||||
|
||||
pev->health = HEADCRAB_HEALTH * 0.25; // less health than full grown
|
||||
}
|
||||
|
||||
void CBabyCrab :: Precache( void )
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL( "models/baby_headcrab.mdl" );
|
||||
CHeadCrab::Precache();
|
||||
}
|
||||
|
||||
|
||||
void CBabyCrab :: SetYawSpeed ( void )
|
||||
{
|
||||
pev->yaw_speed = 120;
|
||||
}
|
||||
|
||||
|
||||
BOOL CBabyCrab :: CheckRangeAttack1( float flDot, float flDist )
|
||||
{
|
||||
if ( pev->flags & FL_ONGROUND )
|
||||
{
|
||||
if ( pev->groundentity && (pev->groundentity->v.flags & (FL_CLIENT|FL_MONSTER)) )
|
||||
return TRUE;
|
||||
|
||||
// A little less accurate, but jump from closer
|
||||
if ( flDist <= 180 && flDot >= 0.55 )
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
Schedule_t* CBabyCrab :: GetScheduleOfType ( int Type )
|
||||
{
|
||||
switch( Type )
|
||||
{
|
||||
case SCHED_FAIL: // If you fail, try to jump!
|
||||
if ( m_hEnemy != NULL )
|
||||
return slHCRangeAttack1Fast;
|
||||
break;
|
||||
|
||||
case SCHED_RANGE_ATTACK1:
|
||||
{
|
||||
return slHCRangeAttack1Fast;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CHeadCrab::GetScheduleOfType( Type );
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,743 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// leech - basic little swimming monster
|
||||
//=========================================================
|
||||
//
|
||||
// UNDONE:
|
||||
// DONE:Steering force model for attack
|
||||
// DONE:Attack animation control / damage
|
||||
// DONE:Establish range of up/down motion and steer around vertical obstacles
|
||||
// DONE:Re-evaluate height periodically
|
||||
// DONE:Fall (MOVETYPE_TOSS) and play different anim if out of water
|
||||
// Test in complex room (c2a3?)
|
||||
// DONE:Sounds? - Kelly will fix
|
||||
// Blood cloud? Hurt effect?
|
||||
// Group behavior?
|
||||
// DONE:Save/restore
|
||||
// Flop animation - just bind to ACT_TWITCH
|
||||
// Fix fatal push into wall case
|
||||
//
|
||||
// Try this on a bird
|
||||
// Try this on a model with hulls/tracehull?
|
||||
//
|
||||
|
||||
|
||||
#include "float.h"
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "defaults.h"
|
||||
|
||||
|
||||
|
||||
// Animation events
|
||||
#define LEECH_AE_ATTACK 1
|
||||
#define LEECH_AE_FLOP 2
|
||||
|
||||
|
||||
// Movement constants
|
||||
|
||||
#define LEECH_ACCELERATE 10
|
||||
#define LEECH_CHECK_DIST 45
|
||||
#define LEECH_SWIM_SPEED 50
|
||||
#define LEECH_SWIM_ACCEL 80
|
||||
#define LEECH_SWIM_DECEL 10
|
||||
#define LEECH_TURN_RATE 90
|
||||
#define LEECH_SIZEX 10
|
||||
#define LEECH_FRAMETIME 0.1
|
||||
|
||||
|
||||
|
||||
#define DEBUG_BEAMS 0
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
#include "basebeams.h"
|
||||
#endif
|
||||
|
||||
|
||||
class CLeech : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
|
||||
void EXPORT SwimThink( void );
|
||||
void EXPORT DeadThink( void );
|
||||
void Touch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( pOther->IsPlayer() )
|
||||
{
|
||||
// If the client is pushing me, give me some velocity
|
||||
if( gpGlobals->trace_ent && gpGlobals->trace_ent == edict() )
|
||||
{
|
||||
pev->velocity = pOther->pev->velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetObjectCollisionBox( void )
|
||||
{
|
||||
pev->absmin = pev->origin + Vector(-8,-8,0);
|
||||
pev->absmax = pev->origin + Vector(8,8,2);
|
||||
}
|
||||
|
||||
void AttackSound( void );
|
||||
void AlertSound( void );
|
||||
void UpdateMotion( void );
|
||||
float ObstacleDistance( CBaseEntity *pTarget );
|
||||
void MakeVectors( void );
|
||||
void RecalculateWaterlevel( void );
|
||||
void SwitchLeechState( void );
|
||||
|
||||
// Base entity functions
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
void Activate( void );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
int Classify( void ) { return CLASS_INSECT; }
|
||||
int IRelationship( CBaseEntity *pTarget );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
|
||||
private:
|
||||
// UNDONE: Remove unused boid vars, do group behavior
|
||||
float m_flTurning;// is this boid turning?
|
||||
BOOL m_fPathBlocked;// TRUE if there is an obstacle ahead
|
||||
float m_flAccelerate;
|
||||
float m_obstacle;
|
||||
float m_top;
|
||||
float m_bottom;
|
||||
float m_height;
|
||||
float m_waterTime;
|
||||
float m_sideTime; // Timer to randomly check clearance on sides
|
||||
float m_zTime;
|
||||
float m_stateTime;
|
||||
float m_attackSoundTime;
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
CBeam *m_pb;
|
||||
CBeam *m_pt;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_leech, CLeech );
|
||||
|
||||
TYPEDESCRIPTION CLeech::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CLeech, m_flTurning, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_fPathBlocked, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CLeech, m_flAccelerate, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_obstacle, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_top, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_bottom, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_height, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( CLeech, m_waterTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_sideTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_zTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_stateTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( CLeech, m_attackSoundTime, FIELD_TIME ),
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CLeech, CBaseMonster );
|
||||
|
||||
|
||||
const char *CLeech::pAttackSounds[] =
|
||||
{
|
||||
"leech/leech_bite1.wav",
|
||||
"leech/leech_bite2.wav",
|
||||
"leech/leech_bite3.wav",
|
||||
};
|
||||
|
||||
const char *CLeech::pAlertSounds[] =
|
||||
{
|
||||
"leech/leech_alert1.wav",
|
||||
"leech/leech_alert2.wav",
|
||||
};
|
||||
|
||||
|
||||
void CLeech::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/leech.mdl");
|
||||
// Just for fun
|
||||
// SET_MODEL(ENT(pev), "models/icky.mdl");
|
||||
|
||||
// UTIL_SetSize( pev, g_vecZero, g_vecZero );
|
||||
UTIL_SetSize( pev, Vector(-1,-1,0), Vector(1,1,2));
|
||||
// Don't push the minz down too much or the water check will fail because this entity is really point-sized
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
SetBits( pev->flags, FL_SWIM );
|
||||
if (pev->health == 0)
|
||||
pev->health = LEECH_HEALTH;
|
||||
|
||||
m_flFieldOfView = -0.5; // 180 degree FOV
|
||||
m_flDistLook = 750;
|
||||
MonsterInit();
|
||||
SetThink(&CLeech:: SwimThink );
|
||||
SetUse( NULL );
|
||||
SetTouch( NULL );
|
||||
pev->view_ofs = g_vecZero;
|
||||
|
||||
m_flTurning = 0;
|
||||
m_fPathBlocked = FALSE;
|
||||
SetActivity( ACT_SWIM );
|
||||
SetState( MONSTERSTATE_IDLE );
|
||||
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 1, 5 );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::Activate( void )
|
||||
{
|
||||
RecalculateWaterlevel();
|
||||
CBaseMonster::Activate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLeech::RecalculateWaterlevel( void )
|
||||
{
|
||||
// Calculate boundaries
|
||||
Vector vecTest = pev->origin - Vector(0,0,400);
|
||||
|
||||
TraceResult tr;
|
||||
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
if ( tr.flFraction != 1.0 )
|
||||
m_bottom = tr.vecEndPos.z + 1;
|
||||
else
|
||||
m_bottom = vecTest.z;
|
||||
|
||||
m_top = UTIL_WaterLevel( pev->origin, pev->origin.z, pev->origin.z + 400 ) - 1;
|
||||
|
||||
// Chop off 20% of the outside range
|
||||
float newBottom = m_bottom * 0.8 + m_top * 0.2;
|
||||
m_top = m_bottom * 0.2 + m_top * 0.8;
|
||||
m_bottom = newBottom;
|
||||
m_height = RANDOM_FLOAT( m_bottom, m_top );
|
||||
m_waterTime = gpGlobals->time + RANDOM_FLOAT( 5, 7 );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::SwitchLeechState( void )
|
||||
{
|
||||
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 3, 6 );
|
||||
if ( m_MonsterState == MONSTERSTATE_COMBAT )
|
||||
{
|
||||
m_hEnemy = NULL;
|
||||
SetState( MONSTERSTATE_IDLE );
|
||||
// We may be up against the player, so redo the side checks
|
||||
m_sideTime = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Look( m_flDistLook );
|
||||
CBaseEntity *pEnemy = BestVisibleEnemy();
|
||||
if ( pEnemy && pEnemy->pev->waterlevel != 0 && pEnemy->pev->watertype != CONTENTS_FOG )
|
||||
{
|
||||
m_hEnemy = pEnemy;
|
||||
SetState( MONSTERSTATE_COMBAT );
|
||||
m_stateTime = gpGlobals->time + RANDOM_FLOAT( 18, 25 );
|
||||
AlertSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CLeech::IRelationship( CBaseEntity *pTarget )
|
||||
{
|
||||
if ( pTarget->IsPlayer() )
|
||||
return R_DL;
|
||||
return CBaseMonster::IRelationship( pTarget );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLeech::AttackSound( void )
|
||||
{
|
||||
if ( gpGlobals->time > m_attackSoundTime )
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
|
||||
m_attackSoundTime = gpGlobals->time + 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLeech::AlertSound( void )
|
||||
{
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM * 0.5, 0, PITCH_NORM );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::Precache( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
//PRECACHE_MODEL("models/icky.mdl");
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/leech.mdl");
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
}
|
||||
|
||||
|
||||
int CLeech::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
pev->velocity = g_vecZero;
|
||||
|
||||
// Nudge the leech away from the damage
|
||||
if ( pevInflictor )
|
||||
{
|
||||
pev->velocity = (pev->origin - pevInflictor->origin).Normalize() * 25;
|
||||
}
|
||||
else if ( pev->movetype == MOVETYPE_TOSS )
|
||||
{
|
||||
ALERT(at_console, "Waterlevel is out\n" );
|
||||
// if ( RANDOM_LONG( 0, 99 ) < 1 )
|
||||
pev->dmg += 2;
|
||||
|
||||
|
||||
}
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
|
||||
void CLeech::HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case LEECH_AE_ATTACK:
|
||||
AttackSound();
|
||||
CBaseEntity *pEnemy;
|
||||
|
||||
pEnemy = m_hEnemy;
|
||||
if ( pEnemy != NULL )
|
||||
{
|
||||
Vector dir, face;
|
||||
|
||||
UTIL_MakeVectorsPrivate( pev->angles, face, NULL, NULL );
|
||||
face.z = 0;
|
||||
dir = (pEnemy->pev->origin - pev->origin);
|
||||
dir.z = 0;
|
||||
dir = dir.Normalize();
|
||||
face = face.Normalize();
|
||||
|
||||
|
||||
if ( DotProduct(dir, face) > 0.9 ) // Only take damage if the leech is facing the prey
|
||||
pEnemy->TakeDamage( pev, pev, LEECH_DMG_BITE, DMG_SLASH );
|
||||
}
|
||||
m_stateTime -= 2;
|
||||
break;
|
||||
|
||||
case LEECH_AE_FLOP:
|
||||
// Play flop sound
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CLeech::MakeVectors( void )
|
||||
{
|
||||
Vector tmp = pev->angles;
|
||||
tmp.x = -tmp.x;
|
||||
UTIL_MakeVectors ( tmp );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ObstacleDistance - returns normalized distance to obstacle
|
||||
//
|
||||
float CLeech::ObstacleDistance( CBaseEntity *pTarget )
|
||||
{
|
||||
TraceResult tr;
|
||||
Vector vecTest;
|
||||
|
||||
// use VELOCITY, not angles, not all boids point the direction they are flying
|
||||
//Vector vecDir = UTIL_VecToAngles( pev->velocity );
|
||||
MakeVectors();
|
||||
|
||||
// check for obstacle ahead
|
||||
vecTest = pev->origin + gpGlobals->v_forward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
|
||||
if ( tr.fStartSolid )
|
||||
{
|
||||
pev->speed = -LEECH_SWIM_SPEED * 0.5;
|
||||
// ALERT( at_console, "Stuck from (%f %f %f) to (%f %f %f)\n", pev->oldorigin.x, pev->oldorigin.y, pev->oldorigin.z, pev->origin.x, pev->origin.y, pev->origin.z );
|
||||
// UTIL_SetOrigin( pev, pev->oldorigin );
|
||||
}
|
||||
|
||||
if ( tr.flFraction != 1.0 )
|
||||
{
|
||||
if ( (pTarget == NULL || tr.pHit != pTarget->edict()) )
|
||||
{
|
||||
return tr.flFraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( fabs(m_height - pev->origin.z) > 10 )
|
||||
return tr.flFraction;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_sideTime < gpGlobals->time )
|
||||
{
|
||||
// extra wide checks
|
||||
vecTest = pev->origin + gpGlobals->v_right * LEECH_SIZEX * 2 + gpGlobals->v_forward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
return tr.flFraction;
|
||||
|
||||
vecTest = pev->origin - gpGlobals->v_right * LEECH_SIZEX * 2 + gpGlobals->v_forward * LEECH_CHECK_DIST;
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
return tr.flFraction;
|
||||
|
||||
// Didn't hit either side, so stop testing for another 0.5 - 1 seconds
|
||||
m_sideTime = gpGlobals->time + RANDOM_FLOAT(0.5,1);
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
|
||||
void CLeech::DeadThink( void )
|
||||
{
|
||||
if ( m_fSequenceFinished )
|
||||
{
|
||||
if ( m_Activity == ACT_DIEFORWARD )
|
||||
{
|
||||
SetThink( NULL );
|
||||
StopAnimation();
|
||||
return;
|
||||
}
|
||||
else if ( pev->flags & FL_ONGROUND )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
SetActivity(ACT_DIEFORWARD);
|
||||
}
|
||||
}
|
||||
StudioFrameAdvance();
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
// Apply damage velocity, but keep out of the walls
|
||||
if ( pev->velocity.x != 0 || pev->velocity.y != 0 )
|
||||
{
|
||||
TraceResult tr;
|
||||
|
||||
// Look 0.5 seconds ahead
|
||||
UTIL_TraceLine(pev->origin, pev->origin + pev->velocity * 0.5, missile, edict(), &tr);
|
||||
if (tr.flFraction != 1.0)
|
||||
{
|
||||
pev->velocity.x = 0;
|
||||
pev->velocity.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CLeech::UpdateMotion( void )
|
||||
{
|
||||
float flapspeed = (pev->speed - m_flAccelerate) / LEECH_ACCELERATE;
|
||||
m_flAccelerate = m_flAccelerate * 0.8 + pev->speed * 0.2;
|
||||
|
||||
if (flapspeed < 0)
|
||||
flapspeed = -flapspeed;
|
||||
flapspeed += 1.0;
|
||||
if (flapspeed < 0.5)
|
||||
flapspeed = 0.5;
|
||||
if (flapspeed > 1.9)
|
||||
flapspeed = 1.9;
|
||||
|
||||
pev->framerate = flapspeed;
|
||||
|
||||
if ( !m_fPathBlocked )
|
||||
pev->avelocity.y = pev->ideal_yaw;
|
||||
else
|
||||
pev->avelocity.y = pev->ideal_yaw * m_obstacle;
|
||||
|
||||
if ( pev->avelocity.y > 150 )
|
||||
m_IdealActivity = ACT_TURN_LEFT;
|
||||
else if ( pev->avelocity.y < -150 )
|
||||
m_IdealActivity = ACT_TURN_RIGHT;
|
||||
else
|
||||
m_IdealActivity = ACT_SWIM;
|
||||
|
||||
// lean
|
||||
float targetPitch, delta;
|
||||
delta = m_height - pev->origin.z;
|
||||
|
||||
if ( delta < -10 )
|
||||
targetPitch = -30;
|
||||
else if ( delta > 10 )
|
||||
targetPitch = 30;
|
||||
else
|
||||
targetPitch = 0;
|
||||
|
||||
pev->angles.x = UTIL_Approach( targetPitch, pev->angles.x, 60 * LEECH_FRAMETIME );
|
||||
|
||||
// bank
|
||||
pev->avelocity.z = - (pev->angles.z + (pev->avelocity.y * 0.25));
|
||||
|
||||
if ( m_MonsterState == MONSTERSTATE_COMBAT && HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
|
||||
m_IdealActivity = ACT_MELEE_ATTACK1;
|
||||
|
||||
// Out of water check
|
||||
if ( !pev->waterlevel || pev->watertype == CONTENTS_FOG )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
m_IdealActivity = ACT_TWITCH;
|
||||
pev->velocity = g_vecZero;
|
||||
|
||||
// Animation will intersect the floor if either of these is non-zero
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
|
||||
if ( pev->framerate < 1.0 )
|
||||
pev->framerate = 1.0;
|
||||
}
|
||||
else if ( pev->movetype == MOVETYPE_TOSS )
|
||||
{
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
RecalculateWaterlevel();
|
||||
ALERT(at_console, "Waterlevel is out\n" );
|
||||
if ( RANDOM_LONG( 0, 99 ) < 1 )
|
||||
{
|
||||
pev->gravity = 0.02;
|
||||
pev->takedamage += 2;
|
||||
}
|
||||
m_waterTime = gpGlobals->time + 2; // Recalc again soon, water may be rising
|
||||
}
|
||||
|
||||
if ( m_Activity != m_IdealActivity )
|
||||
{
|
||||
SetActivity ( m_IdealActivity );
|
||||
}
|
||||
float flInterval = StudioFrameAdvance();
|
||||
DispatchAnimEvents ( flInterval );
|
||||
|
||||
#if DEBUG_BEAMS
|
||||
if ( !m_pb )
|
||||
m_pb = CBeam::BeamCreate( "sprites/laserbeam.spr", 5 );
|
||||
if ( !m_pt )
|
||||
m_pt = CBeam::BeamCreate( "sprites/laserbeam.spr", 5 );
|
||||
m_pb->PointsInit( pev->origin, pev->origin + gpGlobals->v_forward * LEECH_CHECK_DIST );
|
||||
m_pt->PointsInit( pev->origin, pev->origin - gpGlobals->v_right * (pev->avelocity.y*0.25) );
|
||||
if ( m_fPathBlocked )
|
||||
{
|
||||
float color = m_obstacle * 30;
|
||||
if ( m_obstacle == 1.0 )
|
||||
color = 0;
|
||||
if ( color > 255 )
|
||||
color = 255;
|
||||
m_pb->SetColor( 255, (int)color, (int)color );
|
||||
}
|
||||
else
|
||||
m_pb->SetColor( 255, 255, 0 );
|
||||
m_pt->SetColor( 0, 0, 255 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CLeech::SwimThink( void )
|
||||
{
|
||||
TraceResult tr;
|
||||
float flLeftSide;
|
||||
float flRightSide;
|
||||
float targetSpeed;
|
||||
float targetYaw = 0;
|
||||
CBaseEntity *pTarget;
|
||||
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() ))
|
||||
{
|
||||
SetNextThink( RANDOM_FLOAT(1,1.5) );
|
||||
pev->velocity = g_vecZero;
|
||||
return;
|
||||
}
|
||||
else
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
targetSpeed = LEECH_SWIM_SPEED;
|
||||
|
||||
if ( m_waterTime < gpGlobals->time )
|
||||
RecalculateWaterlevel();
|
||||
|
||||
if ( m_stateTime < gpGlobals->time )
|
||||
SwitchLeechState();
|
||||
|
||||
ClearConditions( bits_COND_CAN_MELEE_ATTACK1 );
|
||||
switch( m_MonsterState )
|
||||
{
|
||||
case MONSTERSTATE_COMBAT:
|
||||
pTarget = m_hEnemy;
|
||||
if ( !pTarget )
|
||||
SwitchLeechState();
|
||||
else
|
||||
{
|
||||
// Chase the enemy's eyes
|
||||
m_height = pTarget->pev->origin.z + pTarget->pev->view_ofs.z - 5;
|
||||
// Clip to viable water area
|
||||
if ( m_height < m_bottom )
|
||||
m_height = m_bottom;
|
||||
else if ( m_height > m_top )
|
||||
m_height = m_top;
|
||||
Vector location = pTarget->pev->origin - pev->origin;
|
||||
location.z += (pTarget->pev->view_ofs.z);
|
||||
if ( location.Length() < 40 )
|
||||
SetConditions( bits_COND_CAN_MELEE_ATTACK1 );
|
||||
// Turn towards target ent
|
||||
targetYaw = UTIL_VecToYaw( location );
|
||||
|
||||
targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( pev->angles.y ) );
|
||||
|
||||
if ( targetYaw < (-LEECH_TURN_RATE*0.75) )
|
||||
targetYaw = (-LEECH_TURN_RATE*0.75);
|
||||
else if ( targetYaw > (LEECH_TURN_RATE*0.75) )
|
||||
targetYaw = (LEECH_TURN_RATE*0.75);
|
||||
else
|
||||
targetSpeed *= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( m_zTime < gpGlobals->time )
|
||||
{
|
||||
float newHeight = RANDOM_FLOAT( m_bottom, m_top );
|
||||
m_height = 0.5 * m_height + 0.5 * newHeight;
|
||||
m_zTime = gpGlobals->time + RANDOM_FLOAT( 1, 4 );
|
||||
}
|
||||
if ( RANDOM_LONG( 0, 100 ) < 10 )
|
||||
targetYaw = RANDOM_LONG( -30, 30 );
|
||||
pTarget = NULL;
|
||||
// oldorigin test
|
||||
if ( (pev->origin - pev->oldorigin).Length() < 1 )
|
||||
{
|
||||
// If leech didn't move, there must be something blocking it, so try to turn
|
||||
m_sideTime = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_obstacle = ObstacleDistance( pTarget );
|
||||
pev->oldorigin = pev->origin;
|
||||
if ( m_obstacle < 0.1 )
|
||||
m_obstacle = 0.1;
|
||||
|
||||
// is the way ahead clear?
|
||||
if ( m_obstacle == 1.0 )
|
||||
{
|
||||
// if the leech is turning, stop the trend.
|
||||
if ( m_flTurning != 0 )
|
||||
{
|
||||
m_flTurning = 0;
|
||||
}
|
||||
|
||||
m_fPathBlocked = FALSE;
|
||||
pev->speed = UTIL_Approach( targetSpeed, pev->speed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME );
|
||||
pev->velocity = gpGlobals->v_forward * pev->speed;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_obstacle = 1.0 / m_obstacle;
|
||||
// IF we get this far in the function, the leader's path is blocked!
|
||||
m_fPathBlocked = TRUE;
|
||||
|
||||
if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid
|
||||
{
|
||||
Vector vecTest;
|
||||
// measure clearance on left and right to pick the best dir to turn
|
||||
vecTest = pev->origin + (gpGlobals->v_right * LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST);
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
flRightSide = tr.flFraction;
|
||||
|
||||
vecTest = pev->origin + (gpGlobals->v_right * -LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST);
|
||||
UTIL_TraceLine(pev->origin, vecTest, missile, edict(), &tr);
|
||||
flLeftSide = tr.flFraction;
|
||||
|
||||
// turn left, right or random depending on clearance ratio
|
||||
float delta = (flRightSide - flLeftSide);
|
||||
if ( delta > 0.1 || (delta > -0.1 && RANDOM_LONG(0,100)<50) )
|
||||
m_flTurning = -LEECH_TURN_RATE;
|
||||
else
|
||||
m_flTurning = LEECH_TURN_RATE;
|
||||
}
|
||||
pev->speed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), pev->speed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle );
|
||||
pev->velocity = gpGlobals->v_forward * pev->speed;
|
||||
}
|
||||
pev->ideal_yaw = m_flTurning + targetYaw;
|
||||
UpdateMotion();
|
||||
}
|
||||
|
||||
|
||||
void CLeech::Killed(entvars_t *pevAttacker, int iGib)
|
||||
{
|
||||
Vector vecSplatDir;
|
||||
TraceResult tr;
|
||||
|
||||
ALERT(at_aiconsole, "Leech: killed\n");
|
||||
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
|
||||
CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner);
|
||||
if (pOwner)
|
||||
pOwner->DeathNotice(pev);
|
||||
|
||||
// When we hit the ground, play the "death_end" activity
|
||||
if( pev->waterlevel && pev->watertype != CONTENTS_FOG )
|
||||
{
|
||||
pev->angles.z = 0;
|
||||
pev->angles.x = 0;
|
||||
pev->origin.z += 1;
|
||||
pev->avelocity = g_vecZero;
|
||||
if ( RANDOM_LONG( 0, 99 ) < 70 )
|
||||
pev->avelocity.y = RANDOM_LONG( -720, 720 );
|
||||
|
||||
pev->gravity = 0.02;
|
||||
ClearBits(pev->flags, FL_ONGROUND);
|
||||
SetActivity( ACT_DIESIMPLE );
|
||||
}
|
||||
else
|
||||
SetActivity( ACT_DIEFORWARD );
|
||||
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
SetThink(&CLeech:: DeadThink );
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#ifndef MONSTEREVENT_H
|
||||
#define MONSTEREVENT_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int event;
|
||||
char *options;
|
||||
} MonsterEvent_t;
|
||||
|
||||
#define MONSTER_EVENT_BODYDROP_LIGHT 2001
|
||||
#define MONSTER_EVENT_BODYDROP_HEAVY 2002
|
||||
|
||||
#define MONSTER_EVENT_SWISHSOUND 2010
|
||||
|
||||
#endif // MONSTEREVENT_H
|
|
@ -0,0 +1,163 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#ifndef MONSTERS_H
|
||||
#define MONSTERS_H
|
||||
|
||||
/*
|
||||
|
||||
===== monsters.h ========================================================
|
||||
|
||||
Header file for monster-related utility code
|
||||
|
||||
*/
|
||||
|
||||
#define MAX_PATH_SIZE 10 // max number of nodes available for a path.
|
||||
|
||||
// CHECKLOCALMOVE result types
|
||||
#define LOCALMOVE_INVALID 0 // move is not possible
|
||||
#define LOCALMOVE_INVALID_DONT_TRIANGULATE 1 // move is not possible, don't try to triangulate
|
||||
#define LOCALMOVE_VALID 2 // move is possible
|
||||
|
||||
// Hit Group standards
|
||||
#define HITGROUP_GENERIC 0
|
||||
#define HITGROUP_HEAD 1
|
||||
#define HITGROUP_CHEST 2
|
||||
#define HITGROUP_STOMACH 3
|
||||
#define HITGROUP_LEFTARM 4
|
||||
#define HITGROUP_RIGHTARM 5
|
||||
#define HITGROUP_LEFTLEG 6
|
||||
#define HITGROUP_RIGHTLEG 7
|
||||
|
||||
|
||||
// Monster Spawnflags
|
||||
#define SF_MONSTER_WAIT_TILL_SEEN 1// spawnflag that makes monsters wait until player can see them before attacking.
|
||||
#define SF_MONSTER_GAG 2 // no idle noises from this monster
|
||||
#define SF_MONSTER_HITMONSTERCLIP 4
|
||||
// 8
|
||||
#define SF_MONSTER_PRISONER 16 // monster won't attack anyone, no one will attacke him.
|
||||
// 32
|
||||
// 64
|
||||
#define SF_MONSTER_NO_YELLOW_BLOBS 128 //LRC- if the monster is stuck, don't give errors or show yellow blobs.
|
||||
//LRC- wasn't implemented. #define SF_MONSTER_WAIT_FOR_SCRIPT 128 //spawnflag that makes monsters wait to check for attacking until the script is done or they've been attacked
|
||||
#define SF_MONSTER_PREDISASTER 256 //this is a predisaster scientist or barney. Influences how they speak.
|
||||
#define SF_MONSTER_NO_WPN_DROP 1024 //LRC- never drop your weapon (player can't pick it up.)
|
||||
//LRC - this clashes with 'not in deathmatch'. Replaced with m_iPlayerReact.
|
||||
//#define SF_MONSTER_INVERT_PLAYERREACT 2048 //LRC- if this monster would usually attack the player, don't attack unless provoked. If you would usually NOT attack the player, attack him.
|
||||
#define SF_MONSTER_FALL_TO_GROUND 0x80000000
|
||||
|
||||
// specialty spawnflags
|
||||
#define SF_MONSTER_TURRET_AUTOACTIVATE 32
|
||||
#define SF_MONSTER_TURRET_STARTINACTIVE 64
|
||||
#define SF_MONSTER_WAIT_UNTIL_PROVOKED 64 // don't attack the player unless provoked
|
||||
|
||||
|
||||
|
||||
// MoveToOrigin stuff
|
||||
#define MOVE_START_TURN_DIST 64 // when this far away from moveGoal, start turning to face next goal
|
||||
#define MOVE_STUCK_DIST 32 // if a monster can't step this far, it is stuck.
|
||||
|
||||
|
||||
// MoveToOrigin stuff
|
||||
#define MOVE_NORMAL 0// normal move in the direction monster is facing
|
||||
#define MOVE_STRAFE 1// moves in direction specified, no matter which way monster is facing
|
||||
|
||||
// spawn flags 256 and above are already taken by the engine
|
||||
extern void UTIL_MoveToOrigin( edict_t* pent, const Vector &vecGoal, float flDist, int iMoveType );
|
||||
|
||||
Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj = 1.0 );
|
||||
Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj = 1.0 );
|
||||
extern DLL_GLOBAL Vector g_vecAttackDir;
|
||||
extern DLL_GLOBAL CONSTANT float g_flMeleeRange;
|
||||
extern DLL_GLOBAL CONSTANT float g_flMediumRange;
|
||||
extern DLL_GLOBAL CONSTANT float g_flLongRange;
|
||||
extern void EjectBrass (const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype );
|
||||
extern void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count );
|
||||
|
||||
BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget );
|
||||
BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize = 0.0 );
|
||||
|
||||
// monster to monster relationship types
|
||||
#define R_AL -2 // (ALLY) pals. Good alternative to R_NO when applicable.
|
||||
#define R_FR -1// (FEAR)will run
|
||||
#define R_NO 0// (NO RELATIONSHIP) disregard
|
||||
#define R_DL 1// (DISLIKE) will attack
|
||||
#define R_HT 2// (HATE)will attack this character instead of any visible DISLIKEd characters
|
||||
#define R_NM 3// (NEMESIS) A monster Will ALWAYS attack its nemsis, no matter what
|
||||
|
||||
|
||||
// these bits represent the monster's memory
|
||||
#define MEMORY_CLEAR 0
|
||||
#define bits_MEMORY_PROVOKED ( 1 << 0 )// right now only used for houndeyes.
|
||||
#define bits_MEMORY_INCOVER ( 1 << 1 )// monster knows it is in a covered position.
|
||||
#define bits_MEMORY_SUSPICIOUS ( 1 << 2 )// Ally is suspicious of the player, and will move to provoked more easily
|
||||
#define bits_MEMORY_PATH_FINISHED ( 1 << 3 )// Finished monster path (just used by big momma for now)
|
||||
#define bits_MEMORY_ON_PATH ( 1 << 4 )// Moving on a path
|
||||
#define bits_MEMORY_MOVE_FAILED ( 1 << 5 )// Movement has already failed
|
||||
#define bits_MEMORY_FLINCHED ( 1 << 6 )// Has already flinched
|
||||
#define bits_MEMORY_KILLED ( 1 << 7 )// HACKHACK -- remember that I've already called my Killed()
|
||||
#define bits_MEMORY_CUSTOM4 ( 1 << 28 ) // Monster-specific memory
|
||||
#define bits_MEMORY_CUSTOM3 ( 1 << 29 ) // Monster-specific memory
|
||||
#define bits_MEMORY_CUSTOM2 ( 1 << 30 ) // Monster-specific memory
|
||||
#define bits_MEMORY_CUSTOM1 ( 1 << 31 ) // Monster-specific memory
|
||||
|
||||
// trigger conditions for scripted AI
|
||||
// these MUST match the CHOICES interface in halflife.fgd for the base monster
|
||||
enum
|
||||
{
|
||||
AITRIGGER_NONE = 0,
|
||||
AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER,
|
||||
AITRIGGER_TAKEDAMAGE,
|
||||
AITRIGGER_HALFHEALTH,
|
||||
AITRIGGER_DEATH,
|
||||
AITRIGGER_SQUADMEMBERDIE,
|
||||
AITRIGGER_SQUADLEADERDIE,
|
||||
AITRIGGER_HEARWORLD,
|
||||
AITRIGGER_HEARPLAYER,
|
||||
AITRIGGER_HEARCOMBAT,
|
||||
AITRIGGER_SEEPLAYER_UNCONDITIONAL,
|
||||
AITRIGGER_SEEPLAYER_NOT_IN_COMBAT,
|
||||
};
|
||||
/*
|
||||
0 : "No Trigger"
|
||||
1 : "See Player"
|
||||
2 : "Take Damage"
|
||||
3 : "50% Health Remaining"
|
||||
4 : "Death"
|
||||
5 : "Squad Member Dead"
|
||||
6 : "Squad Leader Dead"
|
||||
7 : "Hear World"
|
||||
8 : "Hear Player"
|
||||
9 : "Hear Combat"
|
||||
*/
|
||||
|
||||
#define CUSTOM_SCHEDULES\
|
||||
virtual Schedule_t *ScheduleFromName( const char *pName );\
|
||||
static Schedule_t *m_scheduleList[];
|
||||
|
||||
#define DEFINE_CUSTOM_SCHEDULES(derivedClass)\
|
||||
Schedule_t *derivedClass::m_scheduleList[] =
|
||||
|
||||
#define IMPLEMENT_CUSTOM_SCHEDULES(derivedClass, baseClass)\
|
||||
Schedule_t *derivedClass::ScheduleFromName( const char *pName )\
|
||||
{\
|
||||
Schedule_t *pSchedule = ScheduleInList( pName, m_scheduleList, ARRAYSIZE(m_scheduleList) );\
|
||||
if ( !pSchedule )\
|
||||
return baseClass::ScheduleFromName(pName);\
|
||||
return pSchedule;\
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif //MONSTERS_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,374 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// nodes.h
|
||||
//=========================================================
|
||||
|
||||
//=========================================================
|
||||
// DEFINE
|
||||
//=========================================================
|
||||
#define MAX_STACK_NODES 100
|
||||
#define NO_NODE -1
|
||||
#define MAX_NODE_HULLS 4
|
||||
|
||||
#define bits_NODE_LAND ( 1 << 0 ) // Land node, so nudge if necessary.
|
||||
#define bits_NODE_AIR ( 1 << 1 ) // Air node, don't nudge.
|
||||
#define bits_NODE_WATER ( 1 << 2 ) // Water node, don't nudge.
|
||||
#define bits_NODE_GROUP_REALM (bits_NODE_LAND | bits_NODE_AIR | bits_NODE_WATER)
|
||||
|
||||
//=========================================================
|
||||
// Instance of a node.
|
||||
//=========================================================
|
||||
class CNode
|
||||
{
|
||||
public:
|
||||
Vector m_vecOrigin;// location of this node in space
|
||||
Vector m_vecOriginPeek; // location of this node (LAND nodes are NODE_HEIGHT higher).
|
||||
BYTE m_Region[3]; // Which of 256 regions do each of the coordinate belong?
|
||||
int m_afNodeInfo;// bits that tell us more about this location
|
||||
|
||||
int m_cNumLinks; // how many links this node has
|
||||
int m_iFirstLink;// index of this node's first link in the link pool.
|
||||
|
||||
// Where to start looking in the compressed routing table (offset into m_pRouteInfo).
|
||||
// (4 hull sizes -- smallest to largest + fly/swim), and secondly, door capability.
|
||||
//
|
||||
int m_pNextBestNode[MAX_NODE_HULLS][2];
|
||||
|
||||
// Used in finding the shortest path. m_fClosestSoFar is -1 if not visited.
|
||||
// Then it is the distance to the source. If another path uses this node
|
||||
// and has a closer distance, then m_iPreviousNode is also updated.
|
||||
//
|
||||
float m_flClosestSoFar; // Used in finding the shortest path.
|
||||
int m_iPreviousNode;
|
||||
|
||||
short m_sHintType;// there is something interesting in the world at this node's position
|
||||
short m_sHintActivity;// there is something interesting in the world at this node's position
|
||||
float m_flHintYaw;// monster on this node should face this yaw to face the hint.
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// CLink - A link between 2 nodes
|
||||
//=========================================================
|
||||
#define bits_LINK_SMALL_HULL ( 1 << 0 )// headcrab box can fit through this connection
|
||||
#define bits_LINK_HUMAN_HULL ( 1 << 1 )// player box can fit through this connection
|
||||
#define bits_LINK_LARGE_HULL ( 1 << 2 )// big box can fit through this connection
|
||||
#define bits_LINK_FLY_HULL ( 1 << 3 )// a flying big box can fit through this connection
|
||||
#define bits_LINK_DISABLED ( 1 << 4 )// link is not valid when the set
|
||||
|
||||
#define NODE_SMALL_HULL 0
|
||||
#define NODE_HUMAN_HULL 1
|
||||
#define NODE_LARGE_HULL 2
|
||||
#define NODE_FLY_HULL 3
|
||||
|
||||
class CLink
|
||||
{
|
||||
public:
|
||||
int m_iSrcNode;// the node that 'owns' this link ( keeps us from having to make reverse lookups )
|
||||
int m_iDestNode;// the node on the other end of the link.
|
||||
|
||||
entvars_t *m_pLinkEnt;// the entity that blocks this connection (doors, etc)
|
||||
|
||||
// m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes)
|
||||
char m_szLinkEntModelname[ 4 ];// the unique name of the brush model that blocks the connection (this is kept for save/restore)
|
||||
|
||||
int m_afLinkInfo;// information about this link
|
||||
float m_flWeight;// length of the link line segment
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int m_SortedBy[3];
|
||||
int m_CheckedEvent;
|
||||
} DIST_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Vector v;
|
||||
short n; // Nearest node or -1 if no node found.
|
||||
} CACHE_ENTRY;
|
||||
|
||||
//=========================================================
|
||||
// CGraph
|
||||
//=========================================================
|
||||
#define GRAPH_VERSION (int)16// !!!increment this whever graph/node/link classes change, to obsolesce older disk files.
|
||||
class CGraph
|
||||
{
|
||||
public:
|
||||
|
||||
// the graph has two flags, and should not be accessed unless both flags are TRUE!
|
||||
BOOL m_fGraphPresent;// is the graph in memory?
|
||||
BOOL m_fGraphPointersSet;// are the entity pointers for the graph all set?
|
||||
BOOL m_fRoutingComplete; // are the optimal routes computed, yet?
|
||||
|
||||
CNode *m_pNodes;// pointer to the memory block that contains all node info
|
||||
CLink *m_pLinkPool;// big list of all node connections
|
||||
char *m_pRouteInfo; // compressed routing information the nodes use.
|
||||
|
||||
int m_cNodes;// total number of nodes
|
||||
int m_cLinks;// total number of links
|
||||
int m_nRouteInfo; // size of m_pRouteInfo in bytes.
|
||||
|
||||
// Tables for making nearest node lookup faster. SortedBy provided nodes in a
|
||||
// order of a particular coordinate. Instead of doing a binary search, RangeStart
|
||||
// and RangeEnd let you get to the part of SortedBy that you are interested in.
|
||||
//
|
||||
// Once you have a point of interest, the only way you'll find a closer point is
|
||||
// if at least one of the coordinates is closer than the ones you have now. So we
|
||||
// search each range. After the search is exhausted, we know we have the closest
|
||||
// node.
|
||||
//
|
||||
#define CACHE_SIZE 128
|
||||
#define NUM_RANGES 256
|
||||
DIST_INFO *m_di; // This is m_cNodes long, but the entries don't correspond to CNode entries.
|
||||
int m_RangeStart[3][NUM_RANGES];
|
||||
int m_RangeEnd[3][NUM_RANGES];
|
||||
float m_flShortest;
|
||||
int m_iNearest;
|
||||
int m_minX, m_minY, m_minZ, m_maxX, m_maxY, m_maxZ;
|
||||
int m_minBoxX, m_minBoxY, m_minBoxZ, m_maxBoxX, m_maxBoxY, m_maxBoxZ;
|
||||
int m_CheckedCounter;
|
||||
float m_RegionMin[3], m_RegionMax[3]; // The range of nodes.
|
||||
CACHE_ENTRY m_Cache[CACHE_SIZE];
|
||||
|
||||
|
||||
int m_HashPrimes[16];
|
||||
short *m_pHashLinks;
|
||||
int m_nHashLinks;
|
||||
|
||||
|
||||
// kinda sleazy. In order to allow variety in active idles for monster groups in a room with more than one node,
|
||||
// we keep track of the last node we searched from and store it here. Subsequent searches by other monsters will pick
|
||||
// up where the last search stopped.
|
||||
int m_iLastActiveIdleSearch;
|
||||
|
||||
// another such system used to track the search for cover nodes, helps greatly with two monsters trying to get to the same node.
|
||||
int m_iLastCoverSearch;
|
||||
|
||||
// functions to create the graph
|
||||
int LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode );
|
||||
int RejectInlineLinks ( CLink *pLinkPool, FILE *file );
|
||||
int FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask);
|
||||
int FindNearestNode ( const Vector &vecOrigin, CBaseEntity *pEntity );
|
||||
int FindNearestNode ( const Vector &vecOrigin, int afNodeTypes );
|
||||
//int FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine );
|
||||
float PathLength( int iStart, int iDest, int iHull, int afCapMask );
|
||||
int NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap );
|
||||
|
||||
enum NODEQUERY { NODEGRAPH_DYNAMIC, NODEGRAPH_STATIC };
|
||||
// A static query means we're asking about the possiblity of handling this entity at ANY time
|
||||
// A dynamic query means we're asking about it RIGHT NOW. So we should query the current state
|
||||
int HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType );
|
||||
entvars_t* LinkEntForLink ( CLink *pLink, CNode *pNode );
|
||||
void ShowNodeConnections ( int iNode );
|
||||
void InitGraph( void );
|
||||
int AllocNodes ( void );
|
||||
|
||||
int CheckNODFile(char *szMapName);
|
||||
int FLoadGraph(char *szMapName);
|
||||
int FSaveGraph(char *szMapName);
|
||||
int FSetGraphPointers(void);
|
||||
void CheckNode(Vector vecOrigin, int iNode);
|
||||
|
||||
void BuildRegionTables(void);
|
||||
void ComputeStaticRoutingTables(void);
|
||||
void TestRoutingTables(void);
|
||||
|
||||
void HashInsert(int iSrcNode, int iDestNode, int iKey);
|
||||
void HashSearch(int iSrcNode, int iDestNode, int &iKey);
|
||||
void HashChoosePrimes(int TableSize);
|
||||
void BuildLinkLookups(void);
|
||||
|
||||
void SortNodes(void);
|
||||
|
||||
int HullIndex( const CBaseEntity *pEntity ); // what hull the monster uses
|
||||
int NodeType( const CBaseEntity *pEntity ); // what node type the monster uses
|
||||
inline int CapIndex( int afCapMask )
|
||||
{
|
||||
if (afCapMask & (bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
inline CNode &Node( int i )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if ( !m_pNodes || i < 0 || i > m_cNodes )
|
||||
ALERT( at_error, "Bad Node!\n" );
|
||||
#endif
|
||||
return m_pNodes[i];
|
||||
}
|
||||
|
||||
inline CLink &Link( int i )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if ( !m_pLinkPool || i < 0 || i > m_cLinks )
|
||||
ALERT( at_error, "Bad link!\n" );
|
||||
#endif
|
||||
return m_pLinkPool[i];
|
||||
}
|
||||
|
||||
inline CLink &NodeLink( int iNode, int iLink )
|
||||
{
|
||||
return Link( Node( iNode ).m_iFirstLink + iLink );
|
||||
}
|
||||
|
||||
inline CLink &NodeLink( const CNode &node, int iLink )
|
||||
{
|
||||
return Link( node.m_iFirstLink + iLink );
|
||||
}
|
||||
|
||||
inline int INodeLink ( int iNode, int iLink )
|
||||
{
|
||||
return NodeLink( iNode, iLink ).m_iDestNode;
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline CNode &SourceNode( int iNode, int iLink )
|
||||
{
|
||||
return Node( NodeLink( iNode, iLink ).m_iSrcNode );
|
||||
}
|
||||
|
||||
inline CNode &DestNode( int iNode, int iLink )
|
||||
{
|
||||
return Node( NodeLink( iNode, iLink ).m_iDestNode );
|
||||
}
|
||||
|
||||
inline CNode *PNodeLink ( int iNode, int iLink )
|
||||
{
|
||||
return &DestNode( iNode, iLink );
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Nodes start out as ents in the level. The node graph
|
||||
// is built, then these ents are discarded.
|
||||
//=========================================================
|
||||
class CNodeEnt : public CBaseEntity
|
||||
{
|
||||
void Spawn( void );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
|
||||
short m_sHintType;
|
||||
short m_sHintActivity;
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CStack - last in, first out.
|
||||
//=========================================================
|
||||
class CStack
|
||||
{
|
||||
public:
|
||||
CStack( void );
|
||||
void Push( int value );
|
||||
int Pop( void );
|
||||
int Top( void );
|
||||
int Empty( void ) { return m_level==0; }
|
||||
int Size( void ) { return m_level; }
|
||||
void CopyToArray ( int *piArray );
|
||||
|
||||
private:
|
||||
int m_stack[ MAX_STACK_NODES ];
|
||||
int m_level;
|
||||
};
|
||||
|
||||
|
||||
//=========================================================
|
||||
// CQueue - first in, first out.
|
||||
//=========================================================
|
||||
class CQueue
|
||||
{
|
||||
public:
|
||||
|
||||
CQueue( void );// constructor
|
||||
inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); }
|
||||
inline int Empty ( void ) { return ( m_cSize == 0 ); }
|
||||
//inline int Tail ( void ) { return ( m_queue[ m_tail ] ); }
|
||||
inline int Size ( void ) { return ( m_cSize ); }
|
||||
void Insert( int, float );
|
||||
int Remove( float & );
|
||||
|
||||
private:
|
||||
int m_cSize;
|
||||
struct tag_QUEUE_NODE
|
||||
{
|
||||
int Id;
|
||||
float Priority;
|
||||
} m_queue[ MAX_STACK_NODES ];
|
||||
int m_head;
|
||||
int m_tail;
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// CQueuePriority - Priority queue (smallest item out first).
|
||||
//
|
||||
//=========================================================
|
||||
class CQueuePriority
|
||||
{
|
||||
public:
|
||||
|
||||
CQueuePriority( void );// constructor
|
||||
inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); }
|
||||
inline int Empty ( void ) { return ( m_cSize == 0 ); }
|
||||
//inline int Tail ( float & ) { return ( m_queue[ m_tail ].Id ); }
|
||||
inline int Size ( void ) { return ( m_cSize ); }
|
||||
void Insert( int, float );
|
||||
int Remove( float &);
|
||||
|
||||
private:
|
||||
int m_cSize;
|
||||
struct tag_HEAP_NODE
|
||||
{
|
||||
int Id;
|
||||
float Priority;
|
||||
} m_heap[ MAX_STACK_NODES ];
|
||||
void Heap_SiftDown(int);
|
||||
void Heap_SiftUp(void);
|
||||
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// hints - these MUST coincide with the HINTS listed under
|
||||
// info_node in the FGD file!
|
||||
//=========================================================
|
||||
enum
|
||||
{
|
||||
HINT_NONE = 0,
|
||||
HINT_WORLD_DOOR,
|
||||
HINT_WORLD_WINDOW,
|
||||
HINT_WORLD_BUTTON,
|
||||
HINT_WORLD_MACHINERY,
|
||||
HINT_WORLD_LEDGE,
|
||||
HINT_WORLD_LIGHT_SOURCE,
|
||||
HINT_WORLD_HEAT_SOURCE,
|
||||
HINT_WORLD_BLINKING_LIGHT,
|
||||
HINT_WORLD_BRIGHT_COLORS,
|
||||
HINT_WORLD_HUMAN_BLOOD,
|
||||
HINT_WORLD_ALIEN_BLOOD,
|
||||
|
||||
HINT_TACTICAL_EXIT = 100,
|
||||
HINT_TACTICAL_VANTAGE,
|
||||
HINT_TACTICAL_AMBUSH,
|
||||
|
||||
HINT_STUKA_PERCH = 300,
|
||||
HINT_STUKA_LANDING,
|
||||
};
|
||||
|
||||
extern CGraph WorldGraph;
|
|
@ -0,0 +1,823 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "client.h"
|
||||
#include "monsters.h"
|
||||
#include "baseweapon.h"
|
||||
#include "nodes.h"
|
||||
#include "soundent.h"
|
||||
#include "basebeams.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int isValid;
|
||||
EHANDLE hGrunt;
|
||||
Vector vecOrigin;
|
||||
Vector vecAngles;
|
||||
} t_ospreygrunt;
|
||||
|
||||
|
||||
|
||||
#define SF_WAITFORTRIGGER 0x40
|
||||
|
||||
|
||||
#define MAX_CARRY 24
|
||||
|
||||
class COsprey : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
int ObjectCaps( void ) { return CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
int Classify( void ) { return CLASS_MACHINE; };
|
||||
int BloodColor( void ) { return DONT_BLEED; }
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
void UpdateGoal( void );
|
||||
BOOL HasDead( void );
|
||||
void EXPORT FlyThink( void );
|
||||
void EXPORT DeployThink( void );
|
||||
void Flight( void );
|
||||
void EXPORT HitTouch( CBaseEntity *pOther );
|
||||
void EXPORT FindAllThink( void );
|
||||
void EXPORT HoverThink( void );
|
||||
CBaseMonster *MakeGrunt( Vector vecSrc );
|
||||
void EXPORT CrashTouch( CBaseEntity *pOther );
|
||||
void EXPORT DyingThink( void );
|
||||
void EXPORT CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
// int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
|
||||
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
void ShowDamage( void );
|
||||
|
||||
CBaseEntity *m_pGoalEnt;
|
||||
Vector m_vel1;
|
||||
Vector m_vel2;
|
||||
Vector m_pos1;
|
||||
Vector m_pos2;
|
||||
Vector m_ang1;
|
||||
Vector m_ang2;
|
||||
float m_startTime;
|
||||
float m_dTime;
|
||||
|
||||
Vector m_velocity;
|
||||
|
||||
float m_flIdealtilt;
|
||||
float m_flRotortilt;
|
||||
|
||||
float m_flRightHealth;
|
||||
float m_flLeftHealth;
|
||||
|
||||
int m_iUnits;
|
||||
EHANDLE m_hGrunt[MAX_CARRY];
|
||||
Vector m_vecOrigin[MAX_CARRY];
|
||||
EHANDLE m_hRepel[4];
|
||||
|
||||
int m_iSoundState;
|
||||
int m_iSpriteTexture;
|
||||
|
||||
int m_iPitch;
|
||||
|
||||
int m_iExplode;
|
||||
int m_iTailGibs;
|
||||
int m_iBodyGibs;
|
||||
int m_iEngineGibs;
|
||||
|
||||
int m_iDoLeftSmokePuff;
|
||||
int m_iDoRightSmokePuff;
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_osprey, COsprey );
|
||||
|
||||
TYPEDESCRIPTION COsprey::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( COsprey, m_pGoalEnt, FIELD_CLASSPTR ),
|
||||
DEFINE_FIELD( COsprey, m_vel1, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_vel2, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_pos1, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_pos2, FIELD_POSITION_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_ang1, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( COsprey, m_ang2, FIELD_VECTOR ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_startTime, FIELD_TIME ),
|
||||
DEFINE_FIELD( COsprey, m_dTime, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( COsprey, m_velocity, FIELD_VECTOR ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_flIdealtilt, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( COsprey, m_flRotortilt, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_flRightHealth, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( COsprey, m_flLeftHealth, FIELD_FLOAT ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_iUnits, FIELD_INTEGER ),
|
||||
DEFINE_ARRAY( COsprey, m_hGrunt, FIELD_EHANDLE, MAX_CARRY ),
|
||||
DEFINE_ARRAY( COsprey, m_vecOrigin, FIELD_POSITION_VECTOR, MAX_CARRY ),
|
||||
DEFINE_ARRAY( COsprey, m_hRepel, FIELD_EHANDLE, 4 ),
|
||||
|
||||
// DEFINE_FIELD( COsprey, m_iSoundState, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( COsprey, m_iSpriteTexture, FIELD_INTEGER ),
|
||||
// DEFINE_FIELD( COsprey, m_iPitch, FIELD_INTEGER ),
|
||||
|
||||
DEFINE_FIELD( COsprey, m_iDoLeftSmokePuff, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( COsprey, m_iDoRightSmokePuff, FIELD_INTEGER ),
|
||||
};
|
||||
IMPLEMENT_SAVERESTORE( COsprey, CBaseMonster );
|
||||
|
||||
void COsprey :: Spawn( void )
|
||||
{
|
||||
Precache( );
|
||||
// motor
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_BBOX;
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/osprey.mdl");
|
||||
UTIL_SetSize(pev, Vector( -400, -400, -100), Vector(400, 400, 32));
|
||||
UTIL_SetOrigin( this, pev->origin );
|
||||
|
||||
// ALERT( at_console, "Osprey origin %f %f %f\n", pev->origin.x, pev->origin.y, pev->origin.z);
|
||||
|
||||
pev->flags |= FL_MONSTER;
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
m_flRightHealth = 200;
|
||||
m_flLeftHealth = 200;
|
||||
pev->health = 400;
|
||||
|
||||
pev->speed = 80; //LRC - default speed, in case path corners don't give a speed.
|
||||
|
||||
m_flFieldOfView = 0; // 180 degrees
|
||||
|
||||
pev->sequence = 0;
|
||||
ResetSequenceInfo( );
|
||||
pev->frame = RANDOM_LONG(0,0xFF);
|
||||
|
||||
InitBoneControllers();
|
||||
|
||||
SetThink(&COsprey :: FindAllThink );
|
||||
SetUse(&COsprey :: CommandUse );
|
||||
|
||||
if (!(pev->spawnflags & SF_WAITFORTRIGGER))
|
||||
{
|
||||
SetNextThink( 1.0 );
|
||||
}
|
||||
|
||||
m_pos2 = pev->origin;
|
||||
m_ang2 = pev->angles;
|
||||
m_vel2 = pev->velocity;
|
||||
}
|
||||
|
||||
|
||||
void COsprey::Precache( void )
|
||||
{
|
||||
UTIL_PrecacheEntity( "monster_human_grunt" );
|
||||
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/osprey.mdl");
|
||||
PRECACHE_MODEL("models/HVR.mdl");
|
||||
|
||||
PRECACHE_SOUND("apache/ap_rotor4.wav");
|
||||
PRECACHE_SOUND("weapons/mortarhit.wav");
|
||||
|
||||
m_iSpriteTexture = PRECACHE_MODEL( "sprites/rope.spr" );
|
||||
|
||||
m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" );
|
||||
m_iTailGibs = PRECACHE_MODEL( "models/osprey_tailgibs.mdl" );
|
||||
m_iBodyGibs = PRECACHE_MODEL( "models/osprey_bodygibs.mdl" );
|
||||
m_iEngineGibs = PRECACHE_MODEL( "models/osprey_enginegibs.mdl" );
|
||||
}
|
||||
|
||||
void COsprey::CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||||
{
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
|
||||
void COsprey :: FindAllThink( void )
|
||||
{
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
m_iUnits = 0;
|
||||
while (m_iUnits < MAX_CARRY && (pEntity = UTIL_FindEntityByClassname( pEntity, "monster_human_grunt" )) != NULL)
|
||||
{
|
||||
if (pEntity->IsAlive())
|
||||
{
|
||||
m_hGrunt[m_iUnits] = pEntity;
|
||||
m_vecOrigin[m_iUnits] = pEntity->pev->origin;
|
||||
m_iUnits++;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_iUnits == 0)
|
||||
{
|
||||
m_iUnits = 4; //LRC - stop whining, just make the damn grunts...
|
||||
|
||||
// ALERT( at_console, "osprey error: no grunts to resupply\n");
|
||||
// UTIL_Remove( this );
|
||||
// return;
|
||||
}
|
||||
SetThink(&COsprey :: FlyThink );
|
||||
SetNextThink( 0.1 );
|
||||
m_startTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: DeployThink( void )
|
||||
{
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
|
||||
Vector vecForward = gpGlobals->v_forward;
|
||||
Vector vecRight = gpGlobals->v_right;
|
||||
Vector vecUp = gpGlobals->v_up;
|
||||
|
||||
Vector vecSrc;
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -4096.0), ignore_monsters, ENT(pev), &tr);
|
||||
CSoundEnt::InsertSound ( bits_SOUND_DANGER, tr.vecEndPos, 400, 0.3 );
|
||||
|
||||
vecSrc = pev->origin + vecForward * 32 + vecRight * 100 + vecUp * -96;
|
||||
m_hRepel[0] = MakeGrunt( vecSrc );
|
||||
|
||||
vecSrc = pev->origin + vecForward * -64 + vecRight * 100 + vecUp * -96;
|
||||
m_hRepel[1] = MakeGrunt( vecSrc );
|
||||
|
||||
vecSrc = pev->origin + vecForward * 32 + vecRight * -100 + vecUp * -96;
|
||||
m_hRepel[2] = MakeGrunt( vecSrc );
|
||||
|
||||
vecSrc = pev->origin + vecForward * -64 + vecRight * -100 + vecUp * -96;
|
||||
m_hRepel[3] = MakeGrunt( vecSrc );
|
||||
|
||||
SetThink(&COsprey :: HoverThink );
|
||||
SetNextThink( 0.1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL COsprey :: HasDead( )
|
||||
{
|
||||
for (int i = 0; i < m_iUnits; i++)
|
||||
{
|
||||
if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vecOrigin[i] = m_hGrunt[i]->pev->origin; // send them to where they died
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
CBaseMonster *COsprey :: MakeGrunt( Vector vecSrc )
|
||||
{
|
||||
CBaseEntity *pEntity;
|
||||
CBaseMonster *pGrunt;
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( vecSrc, vecSrc + Vector( 0, 0, -4096.0), dont_ignore_monsters, ENT(pev), &tr);
|
||||
if ( tr.pHit && Instance( tr.pHit )->pev->solid != SOLID_BSP)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < m_iUnits; i++)
|
||||
{
|
||||
if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
|
||||
{
|
||||
pEntity = Create( "monster_human_grunt", vecSrc, pev->angles );
|
||||
pGrunt = pEntity->MyMonsterPointer( );
|
||||
pGrunt->pev->movetype = MOVETYPE_FLY;
|
||||
pGrunt->pev->velocity = Vector( 0, 0, RANDOM_FLOAT( -196, -128 ) );
|
||||
pGrunt->SetActivity( ACT_GLIDE );
|
||||
|
||||
CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.spr", 10 );
|
||||
pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt->entindex() );
|
||||
pBeam->SetFlags( BEAM_FSOLID );
|
||||
pBeam->SetColor( 255, 255, 255 );
|
||||
pBeam->SetThink( Remove );
|
||||
pBeam->SetNextThink( -4096.0 * tr.flFraction / pGrunt->pev->velocity.z + 0.5 );
|
||||
|
||||
// ALERT( at_console, "%d at %.0f %.0f %.0f\n", i, m_vecOrigin[i].x, m_vecOrigin[i].y, m_vecOrigin[i].z );
|
||||
pGrunt->m_vecLastPosition = m_vecOrigin[i];
|
||||
m_hGrunt[i] = pGrunt;
|
||||
return pGrunt;
|
||||
}
|
||||
}
|
||||
// ALERT( at_console, "none dead\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: HoverThink( void )
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_hRepel[i] != NULL && m_hRepel[i]->pev->health > 0 && !(m_hRepel[i]->pev->flags & FL_ONGROUND))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 4)
|
||||
{
|
||||
m_startTime = gpGlobals->time;
|
||||
SetThink(&COsprey :: FlyThink );
|
||||
}
|
||||
|
||||
SetNextThink( 0.1 );
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
ShowDamage( );
|
||||
}
|
||||
|
||||
|
||||
void COsprey::UpdateGoal( )
|
||||
{
|
||||
if (m_pGoalEnt)
|
||||
{
|
||||
m_pos1 = m_pos2;
|
||||
m_ang1 = m_ang2;
|
||||
m_vel1 = m_vel2;
|
||||
m_pos2 = m_pGoalEnt->pev->origin;
|
||||
m_ang2 = m_pGoalEnt->pev->angles;
|
||||
UTIL_MakeAimVectors( Vector( 0, m_ang2.y, 0 ) );
|
||||
|
||||
//LRC - ugh. we shouldn't require our path corners to specify a speed!
|
||||
if (m_pGoalEnt->pev->speed)
|
||||
pev->speed = m_pGoalEnt->pev->speed;
|
||||
|
||||
m_vel2 = gpGlobals->v_forward * pev->speed; //LRC
|
||||
|
||||
m_startTime = m_startTime + m_dTime;
|
||||
m_dTime = 2.0 * (m_pos1 - m_pos2).Length() / (m_vel1.Length() + pev->speed);
|
||||
|
||||
// ALERT(at_console, "osprey m_dTime = %f / %f + %f\n", (m_pos1 - m_pos2).Length(), m_vel1.Length(), m_pGoalEnt->pev->speed);
|
||||
|
||||
if (m_ang1.y - m_ang2.y < -180)
|
||||
{
|
||||
m_ang1.y += 360;
|
||||
}
|
||||
else if (m_ang1.y - m_ang2.y > 180)
|
||||
{
|
||||
m_ang1.y -= 360;
|
||||
}
|
||||
|
||||
if (pev->speed < 400)
|
||||
m_flIdealtilt = 0;
|
||||
else
|
||||
m_flIdealtilt = -90;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALERT( at_console, "osprey missing target");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey::FlyThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target
|
||||
{
|
||||
m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) );
|
||||
UpdateGoal( );
|
||||
}
|
||||
|
||||
if (gpGlobals->time > m_startTime + m_dTime)
|
||||
{
|
||||
if (m_pGoalEnt->pev->speed == 0)
|
||||
{
|
||||
SetThink(&COsprey:: DeployThink );
|
||||
}
|
||||
int loopbreaker = 100; //LRC - <slap> don't loop indefinitely!
|
||||
do {
|
||||
m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_pGoalEnt->pev->target ) );
|
||||
loopbreaker--; //LRC
|
||||
} while (m_pGoalEnt->pev->speed < 400 && !HasDead() && loopbreaker > 0);
|
||||
UpdateGoal( );
|
||||
}
|
||||
|
||||
Flight( );
|
||||
ShowDamage( );
|
||||
}
|
||||
|
||||
|
||||
void COsprey::Flight( )
|
||||
{
|
||||
float t = (gpGlobals->time - m_startTime);
|
||||
float scale = 1.0 / m_dTime;
|
||||
|
||||
float f = UTIL_SplineFraction( t * scale, 1.0 );
|
||||
|
||||
// ALERT(at_console, "Osprey setorigin m_pos1 %f, m_vel1 %f, m_pos2 %f, m_vel2 %f, m_dTime %f, t %f, f %f\n", m_pos1.x, m_vel1.x, m_pos2.x, m_vel2.x, m_dTime, t, f);
|
||||
|
||||
Vector pos = (m_pos1 + m_vel1 * t) * (1.0 - f) + (m_pos2 - m_vel2 * (m_dTime - t)) * f;
|
||||
Vector ang = (m_ang1) * (1.0 - f) + (m_ang2) * f;
|
||||
m_velocity = m_vel1 * (1.0 - f) + m_vel2 * f;
|
||||
|
||||
UTIL_SetOrigin( this, pos );
|
||||
pev->angles = ang;
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
float flSpeed = DotProduct( gpGlobals->v_forward, m_velocity );
|
||||
|
||||
// float flSpeed = DotProduct( gpGlobals->v_forward, pev->velocity );
|
||||
|
||||
float m_flIdealtilt = (160 - flSpeed) / 10.0;
|
||||
|
||||
// ALERT( at_console, "%f %f\n", flSpeed, flIdealtilt );
|
||||
if (m_flRotortilt < m_flIdealtilt)
|
||||
{
|
||||
m_flRotortilt += 0.5;
|
||||
if (m_flRotortilt > 0)
|
||||
m_flRotortilt = 0;
|
||||
}
|
||||
if (m_flRotortilt > m_flIdealtilt)
|
||||
{
|
||||
m_flRotortilt -= 0.5;
|
||||
if (m_flRotortilt < -90)
|
||||
m_flRotortilt = -90;
|
||||
}
|
||||
SetBoneController( 0, m_flRotortilt );
|
||||
|
||||
|
||||
if (m_iSoundState == 0)
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, 0, 110 );
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", 0.5, 0.2, 0, 110 );
|
||||
|
||||
m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
|
||||
}
|
||||
else
|
||||
{
|
||||
CBaseEntity *pPlayer = NULL;
|
||||
|
||||
pPlayer = UTIL_FindEntityByClassname( NULL, "player" );
|
||||
// UNDONE: this needs to send different sounds to every player for multiplayer.
|
||||
if (pPlayer)
|
||||
{
|
||||
float pitch = DotProduct( m_velocity - pPlayer->pev->velocity, (pPlayer->pev->origin - pev->origin).Normalize() );
|
||||
|
||||
pitch = (int)(100 + pitch / 75.0);
|
||||
|
||||
if (pitch > 250)
|
||||
pitch = 250;
|
||||
if (pitch < 50)
|
||||
pitch = 50;
|
||||
|
||||
if (pitch == 100)
|
||||
pitch = 101;
|
||||
|
||||
if (pitch != m_iPitch)
|
||||
{
|
||||
m_iPitch = pitch;
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav", 1.0, 0.15, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
// ALERT( at_console, "%.0f\n", pitch );
|
||||
}
|
||||
}
|
||||
// EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", flVol, 0.2, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void COsprey::HitTouch( CBaseEntity *pOther )
|
||||
{
|
||||
SetNextThink( 2.0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
int COsprey::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
if (m_flRotortilt <= -90)
|
||||
{
|
||||
m_flRotortilt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flRotortilt -= 45;
|
||||
}
|
||||
SetBoneController( 0, m_flRotortilt );
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void COsprey :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->movetype = MOVETYPE_TOSS;
|
||||
pev->gravity = 0.3;
|
||||
pev->velocity = m_velocity;
|
||||
pev->avelocity = Vector( RANDOM_FLOAT( -20, 20 ), 0, RANDOM_FLOAT( -50, 50 ) );
|
||||
STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor4.wav" );
|
||||
|
||||
UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) );
|
||||
SetThink(&COsprey :: DyingThink );
|
||||
SetTouch(&COsprey :: CrashTouch );
|
||||
SetNextThink( 0.1 );
|
||||
pev->health = 0;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
m_startTime = gpGlobals->time + 4.0;
|
||||
}
|
||||
|
||||
void COsprey::CrashTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// only crash if we hit something solid
|
||||
if ( pOther->pev->solid == SOLID_BSP)
|
||||
{
|
||||
SetTouch( NULL );
|
||||
m_startTime = gpGlobals->time;
|
||||
SetNextThink( 0 );
|
||||
m_velocity = pev->velocity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: DyingThink( void )
|
||||
{
|
||||
StudioFrameAdvance( );
|
||||
SetNextThink( 0.1 );
|
||||
|
||||
pev->avelocity = pev->avelocity * 1.02;
|
||||
|
||||
// still falling?
|
||||
if (m_startTime > gpGlobals->time )
|
||||
{
|
||||
UTIL_MakeAimVectors( pev->angles );
|
||||
ShowDamage( );
|
||||
|
||||
Vector vecSpot = pev->origin + pev->velocity * 0.2;
|
||||
|
||||
// random explosions
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now
|
||||
WRITE_COORD( vecSpot.x + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.y + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.z + RANDOM_FLOAT( -150, -50 ));
|
||||
WRITE_SHORT( g_sModelIndexFireball );
|
||||
WRITE_BYTE( RANDOM_LONG(0,29) + 30 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
WRITE_BYTE( TE_EXPLFLAG_NONE );
|
||||
MESSAGE_END();
|
||||
|
||||
// lots of smoke
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSpot.x + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.y + RANDOM_FLOAT( -150, 150 ));
|
||||
WRITE_COORD( vecSpot.z + RANDOM_FLOAT( -150, -50 ));
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 100 ); // scale * 10
|
||||
WRITE_BYTE( 10 ); // framerate
|
||||
MESSAGE_END();
|
||||
|
||||
|
||||
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z );
|
||||
|
||||
// size
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 132 );
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( pev->velocity.x );
|
||||
WRITE_COORD( pev->velocity.y );
|
||||
WRITE_COORD( pev->velocity.z );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 50 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_iTailGibs ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 8 ); // let client decide
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 200 );// 10.0 seconds
|
||||
|
||||
// flags
|
||||
|
||||
WRITE_BYTE( BREAK_METAL );
|
||||
MESSAGE_END();
|
||||
|
||||
|
||||
|
||||
// don't stop it we touch a entity
|
||||
pev->flags &= ~FL_ONGROUND;
|
||||
SetNextThink( 0.2 );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
|
||||
/*
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
||||
WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 512 );
|
||||
WRITE_SHORT( m_iExplode );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 10 ); // framerate
|
||||
MESSAGE_END();
|
||||
*/
|
||||
|
||||
// gibs
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_SPRITE );
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 512 );
|
||||
WRITE_SHORT( m_iExplode );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 255 ); // brightness
|
||||
MESSAGE_END();
|
||||
|
||||
/*
|
||||
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 300 );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( 250 ); // scale * 10
|
||||
WRITE_BYTE( 6 ); // framerate
|
||||
MESSAGE_END();
|
||||
*/
|
||||
|
||||
// blast circle
|
||||
MESSAGE_BEGIN( MSG_PAS, gmsg.TempEntity, pev->origin );
|
||||
WRITE_BYTE( TE_BEAMCYLINDER );
|
||||
WRITE_COORD( pev->origin.x);
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z);
|
||||
WRITE_COORD( pev->origin.x);
|
||||
WRITE_COORD( pev->origin.y);
|
||||
WRITE_COORD( pev->origin.z + 2000 ); // reach damage radius over .2 seconds
|
||||
WRITE_SHORT( m_iSpriteTexture );
|
||||
WRITE_BYTE( 0 ); // startframe
|
||||
WRITE_BYTE( 0 ); // framerate
|
||||
WRITE_BYTE( 4 ); // life
|
||||
WRITE_BYTE( 32 ); // width
|
||||
WRITE_BYTE( 0 ); // noise
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 255 ); // r, g, b
|
||||
WRITE_BYTE( 192 ); // r, g, b
|
||||
WRITE_BYTE( 128 ); // brightness
|
||||
WRITE_BYTE( 0 ); // speed
|
||||
MESSAGE_END();
|
||||
|
||||
EMIT_SOUND(ENT(pev), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3);
|
||||
|
||||
RadiusDamage( pev->origin, pev, pev, 300, CLASS_NONE, DMG_BLAST );
|
||||
|
||||
// gibs
|
||||
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
|
||||
MESSAGE_BEGIN( MSG_PAS, gmsg.TempEntity, vecSpot );
|
||||
WRITE_BYTE( TE_BREAKMODEL);
|
||||
|
||||
// position
|
||||
WRITE_COORD( vecSpot.x );
|
||||
WRITE_COORD( vecSpot.y );
|
||||
WRITE_COORD( vecSpot.z + 64);
|
||||
|
||||
// size
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 800 );
|
||||
WRITE_COORD( 128 );
|
||||
|
||||
// velocity
|
||||
WRITE_COORD( m_velocity.x );
|
||||
WRITE_COORD( m_velocity.y );
|
||||
WRITE_COORD( fabs( m_velocity.z ) * 0.25 );
|
||||
|
||||
// randomization
|
||||
WRITE_BYTE( 40 );
|
||||
|
||||
// Model
|
||||
WRITE_SHORT( m_iBodyGibs ); //model id#
|
||||
|
||||
// # of shards
|
||||
WRITE_BYTE( 128 );
|
||||
|
||||
// duration
|
||||
WRITE_BYTE( 200 );// 10.0 seconds
|
||||
|
||||
// flags
|
||||
|
||||
WRITE_BYTE( BREAK_METAL );
|
||||
MESSAGE_END();
|
||||
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey :: ShowDamage( void )
|
||||
{
|
||||
if (m_iDoLeftSmokePuff > 0 || RANDOM_LONG(0,99) > m_flLeftHealth)
|
||||
{
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_right * -340;
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSrc );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSrc.x );
|
||||
WRITE_COORD( vecSrc.y );
|
||||
WRITE_COORD( vecSrc.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
if (m_iDoLeftSmokePuff > 0)
|
||||
m_iDoLeftSmokePuff--;
|
||||
}
|
||||
if (m_iDoRightSmokePuff > 0 || RANDOM_LONG(0,99) > m_flRightHealth)
|
||||
{
|
||||
Vector vecSrc = pev->origin + gpGlobals->v_right * 340;
|
||||
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, vecSrc );
|
||||
WRITE_BYTE( TE_SMOKE );
|
||||
WRITE_COORD( vecSrc.x );
|
||||
WRITE_COORD( vecSrc.y );
|
||||
WRITE_COORD( vecSrc.z );
|
||||
WRITE_SHORT( g_sModelIndexSmoke );
|
||||
WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10
|
||||
WRITE_BYTE( 12 ); // framerate
|
||||
MESSAGE_END();
|
||||
if (m_iDoRightSmokePuff > 0)
|
||||
m_iDoRightSmokePuff--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COsprey::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
|
||||
{
|
||||
// ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage );
|
||||
|
||||
// only so much per engine
|
||||
if (ptr->iHitgroup == 3)
|
||||
{
|
||||
if (m_flRightHealth < 0)
|
||||
return;
|
||||
else
|
||||
m_flRightHealth -= flDamage;
|
||||
m_iDoLeftSmokePuff = 3 + (flDamage / 5.0);
|
||||
}
|
||||
|
||||
if (ptr->iHitgroup == 2)
|
||||
{
|
||||
if (m_flLeftHealth < 0)
|
||||
return;
|
||||
else
|
||||
m_flLeftHealth -= flDamage;
|
||||
m_iDoRightSmokePuff = 3 + (flDamage / 5.0);
|
||||
}
|
||||
|
||||
// hit hard, hits cockpit, hits engines
|
||||
if (flDamage > 50 || ptr->iHitgroup == 1 || ptr->iHitgroup == 2 || ptr->iHitgroup == 3)
|
||||
{
|
||||
// ALERT( at_console, "%.0f\n", flDamage );
|
||||
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
|
||||
}
|
||||
else
|
||||
{
|
||||
UTIL_Sparks( ptr->vecEndPos );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,376 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#define PLAYER_FATAL_FALL_SPEED 1024 // approx 60 feet
|
||||
#define PLAYER_MAX_SAFE_FALL_SPEED 580 // approx 20 feet
|
||||
#define DAMAGE_FOR_FALL_SPEED (float) 100 / ( PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED )// damage per unit per second.
|
||||
#define PLAYER_MIN_BOUNCE_SPEED 200
|
||||
#define PLAYER_FALL_PUNCH_THRESHHOLD (float) 350 // won't punch player's screen/make scrape noise unless player falling at least this fast.
|
||||
|
||||
#define CBTEXTURENAMEMAX 13 // only load first n chars of name
|
||||
|
||||
#define CHAR_TEX_CONCRETE 'C' // texture types
|
||||
#define CHAR_TEX_METAL 'M'
|
||||
#define CHAR_TEX_DIRT 'D'
|
||||
#define CHAR_TEX_VENT 'V'
|
||||
#define CHAR_TEX_GRATE 'G'
|
||||
#define CHAR_TEX_TILE 'T'
|
||||
#define CHAR_TEX_SLOSH 'S'
|
||||
#define CHAR_TEX_WOOD 'W'
|
||||
#define CHAR_TEX_COMPUTER 'P'
|
||||
#define CHAR_TEX_GLASS 'Y'
|
||||
#define CHAR_TEX_FLESH 'F'
|
||||
|
||||
//
|
||||
// Player PHYSICS FLAGS bits
|
||||
//
|
||||
#define PFLAG_ONLADDER ( 1<<0 )
|
||||
#define PFLAG_ONSWING ( 1<<0 )
|
||||
#define PFLAG_ONTRAIN ( 1<<1 )
|
||||
#define PFLAG_ONBARNACLE ( 1<<2 )
|
||||
#define PFLAG_DUCKING ( 1<<3 ) // In the process of ducking, but totally squatted yet
|
||||
#define PFLAG_USING ( 1<<4 ) // Using a continuous entity
|
||||
#define PFLAG_OBSERVER ( 1<<5 ) // player is locked in stationary cam mode. Spectators can move, observers can't.
|
||||
|
||||
//
|
||||
// generic player
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
//This is Half-Life player entity
|
||||
//-----------------------------------------------------
|
||||
#define CSUITPLAYLIST 4 // max of 4 suit sentences queued up at any time
|
||||
|
||||
#define SUIT_GROUP TRUE
|
||||
#define SUIT_SENTENCE FALSE
|
||||
|
||||
#define SUIT_REPEAT_OK 0
|
||||
#define SUIT_NEXT_IN_30SEC 30
|
||||
#define SUIT_NEXT_IN_1MIN 60
|
||||
#define SUIT_NEXT_IN_5MIN 300
|
||||
#define SUIT_NEXT_IN_10MIN 600
|
||||
#define SUIT_NEXT_IN_30MIN 1800
|
||||
#define SUIT_NEXT_IN_1HOUR 3600
|
||||
|
||||
#define CSUITNOREPEAT 32
|
||||
|
||||
#define SOUND_FLASHLIGHT_ON "items/flashlight1.wav"
|
||||
#define SOUND_FLASHLIGHT_OFF "items/flashlight1.wav"
|
||||
|
||||
#define TEAM_NAME_LENGTH 16
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PLAYER_IDLE,
|
||||
PLAYER_WALK,
|
||||
PLAYER_JUMP,
|
||||
PLAYER_SUPERJUMP,
|
||||
PLAYER_DIE,
|
||||
PLAYER_ATTACK1,
|
||||
} PLAYER_ANIM;
|
||||
|
||||
|
||||
//NB: changing this structure will cause problems! --LRC
|
||||
|
||||
#define MAX_ID_RANGE 2048
|
||||
#define SBAR_STRING_SIZE 128
|
||||
|
||||
enum sbar_data
|
||||
{
|
||||
SBAR_ID_TARGETNAME = 1,
|
||||
SBAR_ID_TARGETHEALTH,
|
||||
SBAR_ID_TARGETARMOR,
|
||||
SBAR_END,
|
||||
};
|
||||
|
||||
#define CHAT_INTERVAL 1.0f
|
||||
|
||||
class CBasePlayer : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
int random_seed; // See that is shared between client & server for shared weapons code
|
||||
|
||||
int m_iPlayerSound;// the index of the sound list slot reserved for this player
|
||||
int m_iTargetVolume;// ideal sound volume.
|
||||
int m_iWeaponVolume;// how loud the player's weapon is right now.
|
||||
int m_iExtraSoundTypes;// additional classification for this weapon's sound
|
||||
int m_iWeaponFlash;// brightness of the weapon flash
|
||||
float m_flStopExtraSoundTime;
|
||||
|
||||
float m_flFlashLightTime; // Time until next battery draw/Recharge
|
||||
int m_iFlashBattery; // Flashlight Battery Draw
|
||||
|
||||
int m_afButtonLast;
|
||||
int m_afButtonPressed;
|
||||
int m_afButtonReleased;
|
||||
|
||||
float m_flFallVelocity;
|
||||
|
||||
int m_rgItems[MAX_ITEMS];
|
||||
int m_fKnownItem; // True when a new item needs to be added
|
||||
int m_fNewAmmo; // True when a new item has been added
|
||||
|
||||
unsigned int m_afPhysicsFlags; // physics flags - set when 'normal' physics should be revisited or overriden
|
||||
float m_fNextSuicideTime; // the time after which the player can next use the suicide command
|
||||
|
||||
|
||||
// these are time-sensitive things that we keep track of
|
||||
float m_flTimeStepSound; // when the last stepping sound was made
|
||||
float m_flTimeWeaponIdle; // when to play another weapon idle animation.
|
||||
float m_flSwimTime; // how long player has been underwater
|
||||
float m_flDuckTime; // how long we've been ducking
|
||||
float m_flWallJumpTime; // how long until next walljump
|
||||
|
||||
float m_flSuitUpdate; // when to play next suit update
|
||||
int m_rgSuitPlayList[CSUITPLAYLIST];// next sentencenum to play for suit update
|
||||
int m_iSuitPlayNext; // next sentence slot for queue storage;
|
||||
int m_rgiSuitNoRepeat[CSUITNOREPEAT]; // suit sentence no repeat list
|
||||
float m_rgflSuitNoRepeatTime[CSUITNOREPEAT]; // how long to wait before allowing repeat
|
||||
int m_lastDamageAmount; // Last damage taken
|
||||
float m_tbdPrev; // Time-based damage timer
|
||||
|
||||
float m_flgeigerRange; // range to nearest radiation source
|
||||
float m_flgeigerDelay; // delay per update of range msg to client
|
||||
int m_igeigerRangePrev;
|
||||
int m_iStepLeft; // alternate left/right foot stepping sound
|
||||
char m_szTextureName[CBTEXTURENAMEMAX]; // current texture name we're standing on
|
||||
char m_chTextureType; // current texture type
|
||||
|
||||
int m_idrowndmg; // track drowning damage taken
|
||||
int m_idrownrestored; // track drowning damage restored
|
||||
|
||||
int m_bitsHUDDamage; // Damage bits for the current fame. These get sent to
|
||||
// the hude via the DAMAGE message
|
||||
BOOL m_fInitHUD; // True when deferred HUD restart msg needs to be sent
|
||||
BOOL m_fGameHUDInitialized;
|
||||
int m_iTrain; // Train control position
|
||||
BOOL m_fWeapon; // Set this to FALSE to force a reset of the current weapon HUD info
|
||||
|
||||
EHANDLE m_pTank; // the tank which the player is currently controlling, NULL if no tank
|
||||
EHANDLE m_pMonitor;
|
||||
float m_fDeadTime; // the time at which the player died (used in PlayerDeathThink())
|
||||
float m_fAirFinished; // moved here from progdefs.h
|
||||
float m_fPainFinished; // moved here from progdefs.h
|
||||
|
||||
BOOL m_fNoPlayerSound; // a debugging feature. Player makes no sound if this is true.
|
||||
BOOL m_fLongJump; // does this player have the longjump module?
|
||||
|
||||
float m_tSneaking;
|
||||
int m_iUpdateTime; // stores the number of frame ticks before sending HUD update messages
|
||||
int m_iClientHealth; // the health currently known by the client. If this changes, send a new
|
||||
int m_iClientBattery; // the Battery currently known by the client. If this changes, send a new
|
||||
int m_iHideHUD; // the players hud weapon info is to be hidden
|
||||
int m_iClientHideHUD;
|
||||
|
||||
// usable player items
|
||||
CBasePlayerWeapon *m_rgpPlayerItems[MAX_ITEM_TYPES];
|
||||
CBasePlayerWeapon *m_pActiveItem;
|
||||
CBasePlayerWeapon *m_pClientActiveItem; // client version of the active item
|
||||
CBasePlayerWeapon *m_pLastItem;
|
||||
CBasePlayerWeapon *m_pNextItem;
|
||||
// shared ammo slots
|
||||
int m_rgAmmo[MAX_AMMO_SLOTS];
|
||||
int m_rgAmmoLast[MAX_AMMO_SLOTS];
|
||||
|
||||
Vector m_vecAutoAim;
|
||||
BOOL m_fOnTarget;
|
||||
int m_iDeaths;
|
||||
float m_iRespawnFrames; // used in PlayerDeathThink() to make sure players can always respawn
|
||||
|
||||
int m_lastx, m_lasty; // These are the previous update's crosshair angles, DON"T SAVE/RESTORE
|
||||
|
||||
int m_nCustomSprayFrames;// Custom clan logo frames for this player
|
||||
float m_flNextDecalTime;// next time this player can spray a decal
|
||||
|
||||
char m_szTeamName[TEAM_NAME_LENGTH];
|
||||
|
||||
virtual void Spawn( void );
|
||||
void Pain( void );
|
||||
|
||||
virtual void Jump( void );
|
||||
virtual void Duck( void );
|
||||
virtual void PreThink( void );
|
||||
virtual void PostThink( void );
|
||||
virtual Vector GetGunPosition( void );
|
||||
virtual int TakeHealth( float flHealth, int bitsDamageType );
|
||||
virtual int TakeArmor( float flArmor, int suit = 0 );
|
||||
virtual int TakeItem( int iItem );
|
||||
virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
|
||||
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType);
|
||||
virtual void Killed( entvars_t *pevAttacker, int iGib );
|
||||
virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ) + pev->view_ofs * RANDOM_FLOAT( 0.5, 1.1 ); }; // position to shoot at
|
||||
virtual void StartSneaking( void ) { m_tSneaking = gpGlobals->time - 1; }
|
||||
virtual void StopSneaking( void ) { m_tSneaking = gpGlobals->time + 30; }
|
||||
virtual BOOL IsSneaking( void ) { return m_tSneaking <= gpGlobals->time; }
|
||||
virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; }
|
||||
virtual BOOL IsPlayer( void ) { return TRUE; } // Spectators should return FALSE for this, they aren't "players" as far as game logic is concerned
|
||||
|
||||
virtual BOOL IsNetClient( void ) { return TRUE; } // Bots should return FALSE for this, they can't receive NET messages
|
||||
// Spectators should return TRUE for this
|
||||
virtual const char *TeamID( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
void RenewItems(void);
|
||||
void PackDeadPlayerItems( void );
|
||||
void RemoveAllItems( BOOL removeSuit );
|
||||
BOOL SwitchWeapon( CBasePlayerWeapon *pWeapon );
|
||||
|
||||
// JOHN: sends custom messages if player HUD data has changed (eg health, ammo)
|
||||
virtual void UpdateClientData( void );
|
||||
|
||||
static TYPEDESCRIPTION m_playerSaveData[];
|
||||
|
||||
// Player is moved across the transition by other means
|
||||
virtual int ObjectCaps( void ) { return CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
||||
virtual void Precache( void );
|
||||
BOOL IsOnLadder( void );
|
||||
BOOL FlashlightIsOn( void );
|
||||
void FlashlightTurnOn( void );
|
||||
void FlashlightTurnOff( void );
|
||||
|
||||
void UpdatePlayerSound ( void );
|
||||
void DeathSound ( void );
|
||||
|
||||
int Classify ( void );
|
||||
void SetAnimation( PLAYER_ANIM playerAnim );
|
||||
void SetWeaponAnimType( const char *szExtention );
|
||||
char m_szAnimExtention[32];
|
||||
|
||||
// custom player functions
|
||||
virtual void ImpulseCommands( void );
|
||||
void CheatImpulseCommands( int iImpulse );
|
||||
|
||||
void StartDeathCam( void );
|
||||
void StartObserver( Vector vecPosition, Vector vecViewAngle );
|
||||
|
||||
void AddPoints( int score, BOOL bAllowNegativeScore );
|
||||
void AddPointsToTeam( int score, BOOL bAllowNegativeScore );
|
||||
BOOL AddPlayerItem( CBasePlayerWeapon *pItem );
|
||||
BOOL RemovePlayerItem( CBasePlayerWeapon *pItem );
|
||||
void DropPlayerItem ( char *pszItemName );
|
||||
BOOL HasPlayerItem( CBasePlayerWeapon *pCheckItem );
|
||||
BOOL HasNamedPlayerItem( const char *pszItemName );
|
||||
BOOL HasWeapons( void );// do I have ANY weapons?
|
||||
void SelectPrevItem( int iItem );
|
||||
void SelectNextItem( int iItem );
|
||||
void SelectLastItem(void);
|
||||
void SelectItem(const char *pstr);
|
||||
void QueueItem(CBasePlayerWeapon *pItem);
|
||||
void ItemPreFrame( void );
|
||||
void ItemPostFrame( void );
|
||||
void GiveNamedItem( const char *szName );
|
||||
void EnableControl(BOOL fControl);
|
||||
|
||||
int GiveAmmo( int iAmount, char *szName, int iMax );
|
||||
void SendAmmoUpdate(void);
|
||||
|
||||
void WaterMove( void );
|
||||
void CheckWaterJump( void );
|
||||
void EXPORT PlayerDeathThink( void );
|
||||
void PlayerUse( void );
|
||||
|
||||
void CheckSuitUpdate();
|
||||
void SetSuitUpdate(char *name, int fgroup, int iNoRepeat);
|
||||
void UpdateGeigerCounter( void );
|
||||
void CheckTimeBasedDamage( void );
|
||||
void UpdateStepSound( void );
|
||||
void PlayStepSound(int step, float fvol);
|
||||
|
||||
BOOL FBecomeProne ( void );
|
||||
void BarnacleVictimBitten ( entvars_t *pevBarnacle );
|
||||
void BarnacleVictimReleased ( void );
|
||||
static int GetAmmoIndex(const char *psz);
|
||||
const char *GetAmmoName(int index );
|
||||
int AmmoInventory( int iAmmoIndex );
|
||||
int Illumination( void );
|
||||
|
||||
void ResetAutoaim( void );
|
||||
Vector GetAutoaimVector( float flDelta );
|
||||
Vector AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta );
|
||||
|
||||
void ForceClientDllUpdate( void ); // Forces all client .dll specific data to be resent to client.
|
||||
|
||||
void DeathMessage( entvars_t *pevKiller );
|
||||
void SendStartMessages( void );
|
||||
|
||||
void SetCustomDecalFrames( int nFrames );
|
||||
int GetCustomDecalFrames( void );
|
||||
|
||||
//Player ID
|
||||
void InitStatusBar( void );
|
||||
void UpdateStatusBar( void );
|
||||
int m_izSBarState[ SBAR_END ];
|
||||
float m_flNextSBarUpdateTime;
|
||||
float m_flStatusBarDisappearDelay;
|
||||
char m_SbarString0[ SBAR_STRING_SIZE ];
|
||||
char m_SbarString1[ SBAR_STRING_SIZE ];
|
||||
// for trigger_viewset
|
||||
CBaseEntity *pViewEnt; // entity to look at
|
||||
int viewFlags; // 1-active, 2-draw hud, 4 - inverse x, 8 - monster look
|
||||
int viewNeedsUpdate; // precache sets to 1, UpdateClientData() sets to 0
|
||||
|
||||
// for trigger_sound
|
||||
byte m_iSndRoomtype; // last roomtype set by sound entity
|
||||
int hearNeedsUpdate; // update DSP contents
|
||||
|
||||
//warhud message
|
||||
int m_iWarHUD;
|
||||
int m_iClientWarHUD;
|
||||
|
||||
//fog variables
|
||||
int m_iFogStartDist;
|
||||
int m_iFogEndDist;
|
||||
int m_iFogFinalEndDist;
|
||||
int m_FogFadeTime;
|
||||
Vector m_FogColor;
|
||||
int fogNeedsUpdate;
|
||||
|
||||
//fade fariables
|
||||
Vector m_FadeColor;
|
||||
int m_FadeAlpha;
|
||||
int m_iFadeFlags;
|
||||
int m_iFadeTime;
|
||||
int m_iFadeHold;
|
||||
int fadeNeedsUpdate;
|
||||
int m_iStartMessage;
|
||||
|
||||
float m_flStartTime; //start time
|
||||
|
||||
float m_flNextChatTime;
|
||||
int Rain_dripsPerSecond;
|
||||
float Rain_windX, Rain_windY;
|
||||
float Rain_randX, Rain_randY;
|
||||
|
||||
int Rain_ideal_dripsPerSecond;
|
||||
float Rain_ideal_windX, Rain_ideal_windY;
|
||||
float Rain_ideal_randX, Rain_ideal_randY;
|
||||
|
||||
float Rain_endFade; // 0 means off
|
||||
float Rain_nextFadeUpdate;
|
||||
|
||||
int rainNeedsUpdate;
|
||||
Vector v_LastAngles;
|
||||
};
|
||||
|
||||
#define AUTOAIM_2DEGREES 0.0348994967025
|
||||
#define AUTOAIM_5DEGREES 0.08715574274766
|
||||
#define AUTOAIM_8DEGREES 0.1391731009601
|
||||
#define AUTOAIM_10DEGREES 0.1736481776669
|
||||
|
||||
extern BOOL gInitHUD;
|
||||
|
||||
#endif // PLAYER_H
|
|
@ -0,0 +1,104 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// rat - environmental monster
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
|
||||
class CRat : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_rat, CRat );
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CRat :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_INSECT; //LRC- maybe someone needs to give them a basic biology lesson...
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CRat :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
switch ( m_Activity )
|
||||
{
|
||||
case ACT_IDLE:
|
||||
default:
|
||||
ys = 45;
|
||||
break;
|
||||
}
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CRat :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/bigrat.mdl");
|
||||
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_RED;
|
||||
pev->health = 8;
|
||||
pev->view_ofs = Vector ( 0, 0, 6 );// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CRat :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/bigrat.mdl");
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
|
@ -0,0 +1,466 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// cockroach
|
||||
//=========================================================
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "soundent.h"
|
||||
#include "decals.h"
|
||||
|
||||
#define ROACH_IDLE 0
|
||||
#define ROACH_BORED 1
|
||||
#define ROACH_SCARED_BY_ENT 2
|
||||
#define ROACH_SCARED_BY_LIGHT 3
|
||||
#define ROACH_SMELL_FOOD 4
|
||||
#define ROACH_EAT 5
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
class CRoach : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
void EXPORT MonsterThink ( void );
|
||||
void Move ( float flInterval );
|
||||
void PickNewDest ( int iCondition );
|
||||
void EXPORT Touch ( CBaseEntity *pOther );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
|
||||
float m_flLastLightLevel;
|
||||
float m_flNextSmellTime;
|
||||
int Classify ( void );
|
||||
void Look ( int iDistance );
|
||||
int ISoundMask ( void );
|
||||
|
||||
// UNDONE: These don't necessarily need to be save/restored, but if we add more data, it may
|
||||
BOOL m_fLightHacked;
|
||||
int m_iMode;
|
||||
// -----------------------------
|
||||
};
|
||||
LINK_ENTITY_TO_CLASS( monster_cockroach, CRoach );
|
||||
|
||||
//=========================================================
|
||||
// ISoundMask - returns a bit mask indicating which types
|
||||
// of sounds this monster regards. In the base class implementation,
|
||||
// monsters care about all sounds, but no scents.
|
||||
//=========================================================
|
||||
int CRoach :: ISoundMask ( void )
|
||||
{
|
||||
return bits_SOUND_CARCASS | bits_SOUND_MEAT;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CRoach :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_INSECT;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Touch
|
||||
//=========================================================
|
||||
void CRoach :: Touch ( CBaseEntity *pOther )
|
||||
{
|
||||
Vector vecSpot;
|
||||
TraceResult tr;
|
||||
|
||||
if ( pOther->pev->velocity == g_vecZero || !pOther->IsPlayer() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down.
|
||||
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr);
|
||||
|
||||
// This isn't really blood. So you don't have to screen it out based on violence levels (UTIL_ShouldShowBlood())
|
||||
UTIL_DecalTrace( &tr, DECAL_YBLOOD1 +RANDOM_LONG(0,5) );
|
||||
|
||||
TakeDamage( pOther->pev, pOther->pev, pev->health, DMG_CRUSH );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CRoach :: SetYawSpeed ( void )
|
||||
{
|
||||
int ys;
|
||||
|
||||
ys = 120;
|
||||
|
||||
pev->yaw_speed = ys;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CRoach :: Spawn()
|
||||
{
|
||||
Precache( );
|
||||
|
||||
if (pev->model)
|
||||
SET_MODEL(ENT(pev), STRING(pev->model)); //LRC
|
||||
else
|
||||
SET_MODEL(ENT(pev), "models/roach.mdl");
|
||||
UTIL_SetSize( pev, Vector( -1, -1, 0 ), Vector( 1, 1, 2 ) );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_YELLOW;
|
||||
pev->effects = 0;
|
||||
pev->health = 1;
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
|
||||
MonsterInit();
|
||||
SetActivity ( ACT_IDLE );
|
||||
|
||||
pev->view_ofs = Vector ( 0, 0, 1 );// position of the eyes relative to monster's origin.
|
||||
pev->takedamage = DAMAGE_YES;
|
||||
m_fLightHacked = FALSE;
|
||||
m_flLastLightLevel = -1;
|
||||
m_iMode = ROACH_IDLE;
|
||||
m_flNextSmellTime = gpGlobals->time;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CRoach :: Precache()
|
||||
{
|
||||
if (pev->model)
|
||||
PRECACHE_MODEL((char*)STRING(pev->model)); //LRC
|
||||
else
|
||||
PRECACHE_MODEL("models/roach.mdl");
|
||||
|
||||
PRECACHE_SOUND("roach/rch_die.wav");
|
||||
PRECACHE_SOUND("roach/rch_walk.wav");
|
||||
PRECACHE_SOUND("roach/rch_smash.wav");
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Killed.
|
||||
//=========================================================
|
||||
void CRoach :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
pev->solid = SOLID_NOT;
|
||||
|
||||
//random sound
|
||||
if ( RANDOM_LONG(0,4) == 1 )
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "roach/rch_die.wav", 0.8, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
else
|
||||
{
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "roach/rch_smash.wav", 0.7, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
|
||||
CSoundEnt::InsertSound ( bits_SOUND_WORLD, pev->origin, 128, 1 );
|
||||
|
||||
CBaseEntity *pOwner = CBaseEntity::Instance(pev->owner);
|
||||
if ( pOwner )
|
||||
{
|
||||
pOwner->DeathNotice( pev );
|
||||
}
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// MonsterThink, overridden for roaches.
|
||||
//=========================================================
|
||||
void CRoach :: MonsterThink( void )
|
||||
{
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() ))
|
||||
SetNextThink( RANDOM_FLOAT(1,1.5) );
|
||||
else
|
||||
SetNextThink( 0.1 );// keep monster thinking
|
||||
|
||||
float flInterval = StudioFrameAdvance( ); // animate
|
||||
|
||||
if ( !m_fLightHacked )
|
||||
{
|
||||
// if light value hasn't been collection for the first time yet,
|
||||
// suspend the creature for a second so the world finishes spawning, then we'll collect the light level.
|
||||
SetNextThink( 1 );
|
||||
m_fLightHacked = TRUE;
|
||||
return;
|
||||
}
|
||||
else if ( m_flLastLightLevel < 0 )
|
||||
{
|
||||
// collect light level for the first time, now that all of the lightmaps in the roach's area have been calculated.
|
||||
m_flLastLightLevel = GETENTITYILLUM( ENT( pev ) );
|
||||
}
|
||||
|
||||
switch ( m_iMode )
|
||||
{
|
||||
case ROACH_IDLE:
|
||||
case ROACH_EAT:
|
||||
{
|
||||
// if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random.
|
||||
if ( RANDOM_LONG(0,3) == 1 )
|
||||
{
|
||||
Look( 150 );
|
||||
if (HasConditions(bits_COND_SEE_FEAR))
|
||||
{
|
||||
// if see something scary
|
||||
//ALERT ( at_aiconsole, "Scared\n" );
|
||||
Eat( 30 + ( RANDOM_LONG(0,14) ) );// roach will ignore food for 30 to 45 seconds
|
||||
PickNewDest( ROACH_SCARED_BY_ENT );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
else if ( RANDOM_LONG(0,149) == 1 )
|
||||
{
|
||||
// if roach doesn't see anything, there's still a chance that it will move. (boredom)
|
||||
//ALERT ( at_aiconsole, "Bored\n" );
|
||||
PickNewDest( ROACH_BORED );
|
||||
SetActivity ( ACT_WALK );
|
||||
|
||||
if ( m_iMode == ROACH_EAT )
|
||||
{
|
||||
// roach will ignore food for 30 to 45 seconds if it got bored while eating.
|
||||
Eat( 30 + ( RANDOM_LONG(0,14) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't do this stuff if eating!
|
||||
if ( m_iMode == ROACH_IDLE )
|
||||
{
|
||||
if ( FShouldEat() )
|
||||
{
|
||||
Listen();
|
||||
}
|
||||
|
||||
if ( GETENTITYILLUM( ENT(pev) ) > m_flLastLightLevel )
|
||||
{
|
||||
// someone turned on lights!
|
||||
//ALERT ( at_console, "Lights!\n" );
|
||||
PickNewDest( ROACH_SCARED_BY_LIGHT );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
else if ( HasConditions(bits_COND_SMELL_FOOD) )
|
||||
{
|
||||
CSound *pSound;
|
||||
|
||||
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
|
||||
|
||||
// roach smells food and is just standing around. Go to food unless food isn't on same z-plane.
|
||||
if ( pSound && abs( pSound->m_vecOrigin.z - pev->origin.z ) <= 3 )
|
||||
{
|
||||
PickNewDest( ROACH_SMELL_FOOD );
|
||||
SetActivity ( ACT_WALK );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ROACH_SCARED_BY_LIGHT:
|
||||
{
|
||||
// if roach was scared by light, then stop if we're over a spot at least as dark as where we started!
|
||||
if ( GETENTITYILLUM( ENT( pev ) ) <= m_flLastLightLevel )
|
||||
{
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flLastLightLevel = GETENTITYILLUM( ENT ( pev ) );// make this our new light level.
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_flGroundSpeed != 0 )
|
||||
{
|
||||
Move( flInterval );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Picks a new spot for roach to run to.(
|
||||
//=========================================================
|
||||
void CRoach :: PickNewDest ( int iCondition )
|
||||
{
|
||||
Vector vecNewDir;
|
||||
Vector vecDest;
|
||||
float flDist;
|
||||
|
||||
m_iMode = iCondition;
|
||||
|
||||
if ( m_iMode == ROACH_SMELL_FOOD )
|
||||
{
|
||||
// find the food and go there.
|
||||
CSound *pSound;
|
||||
|
||||
pSound = CSoundEnt::SoundPointerForIndex( m_iAudibleList );
|
||||
|
||||
if ( pSound )
|
||||
{
|
||||
m_Route[ 0 ].vecLocation.x = pSound->m_vecOrigin.x + ( 3 - RANDOM_LONG(0,5) );
|
||||
m_Route[ 0 ].vecLocation.y = pSound->m_vecOrigin.y + ( 3 - RANDOM_LONG(0,5) );
|
||||
m_Route[ 0 ].vecLocation.z = pSound->m_vecOrigin.z;
|
||||
m_Route[ 0 ].iType = bits_MF_TO_LOCATION;
|
||||
m_movementGoal = RouteClassify( m_Route[ 0 ].iType );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// picks a random spot, requiring that it be at least 128 units away
|
||||
// else, the roach will pick a spot too close to itself and run in
|
||||
// circles. this is a hack but buys me time to work on the real monsters.
|
||||
vecNewDir.x = RANDOM_FLOAT( -1, 1 );
|
||||
vecNewDir.y = RANDOM_FLOAT( -1, 1 );
|
||||
flDist = 256 + ( RANDOM_LONG(0,255) );
|
||||
vecDest = pev->origin + vecNewDir * flDist;
|
||||
|
||||
} while ( ( vecDest - pev->origin ).Length2D() < 128 );
|
||||
|
||||
m_Route[ 0 ].vecLocation.x = vecDest.x;
|
||||
m_Route[ 0 ].vecLocation.y = vecDest.y;
|
||||
m_Route[ 0 ].vecLocation.z = pev->origin.z;
|
||||
m_Route[ 0 ].iType = bits_MF_TO_LOCATION;
|
||||
m_movementGoal = RouteClassify( m_Route[ 0 ].iType );
|
||||
|
||||
if ( RANDOM_LONG(0,9) == 1 )
|
||||
{
|
||||
// every once in a while, a roach will play a skitter sound when they decide to run
|
||||
EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "roach/rch_walk.wav", 1, ATTN_NORM, 0, 80 + RANDOM_LONG(0,39) );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// roach's move function
|
||||
//=========================================================
|
||||
void CRoach :: Move ( float flInterval )
|
||||
{
|
||||
float flWaypointDist;
|
||||
Vector vecApex;
|
||||
|
||||
// local move to waypoint.
|
||||
flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length2D();
|
||||
MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation );
|
||||
|
||||
ChangeYaw ( pev->yaw_speed );
|
||||
UTIL_MakeVectors( pev->angles );
|
||||
|
||||
if ( RANDOM_LONG(0,7) == 1 )
|
||||
{
|
||||
// randomly check for blocked path.(more random load balancing)
|
||||
if ( !WALK_MOVE( ENT(pev), pev->ideal_yaw, 4, WALKMOVE_NORMAL ) )
|
||||
{
|
||||
// stuck, so just pick a new spot to run off to
|
||||
PickNewDest( m_iMode );
|
||||
}
|
||||
}
|
||||
|
||||
WALK_MOVE( ENT(pev), pev->ideal_yaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL );
|
||||
|
||||
// if the waypoint is closer than step size, then stop after next step (ok for roach to overshoot)
|
||||
if ( flWaypointDist <= m_flGroundSpeed * flInterval )
|
||||
{
|
||||
// take truncated step and stop
|
||||
|
||||
SetActivity ( ACT_IDLE );
|
||||
m_flLastLightLevel = GETENTITYILLUM( ENT ( pev ) );// this is roach's new comfortable light level
|
||||
|
||||
if ( m_iMode == ROACH_SMELL_FOOD )
|
||||
{
|
||||
m_iMode = ROACH_EAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iMode = ROACH_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( RANDOM_LONG(0,149) == 1 && m_iMode != ROACH_SCARED_BY_LIGHT && m_iMode != ROACH_SMELL_FOOD )
|
||||
{
|
||||
// random skitter while moving as long as not on a b-line to get out of light or going to food
|
||||
PickNewDest( FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Look - overriden for the roach, which can virtually see
|
||||
// 360 degrees.
|
||||
//=========================================================
|
||||
void CRoach :: Look ( int iDistance )
|
||||
{
|
||||
CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with
|
||||
CBaseEntity *pPreviousEnt;// the last entity added to the link list
|
||||
int iSighted = 0;
|
||||
|
||||
// DON'T let visibility information from last frame sit around!
|
||||
ClearConditions( bits_COND_SEE_HATE |bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR );
|
||||
|
||||
// don't let monsters outside of the player's PVS act up, or most of the interesting
|
||||
// things will happen before the player gets there!
|
||||
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) && !HaveCamerasInPVS( edict() ))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_pLink = NULL;
|
||||
pPreviousEnt = this;
|
||||
|
||||
// Does sphere also limit itself to PVS?
|
||||
// Examine all entities within a reasonable radius
|
||||
// !!!PERFORMANCE - let's trivially reject the ent list before radius searching!
|
||||
while ((pSightEnt = UTIL_FindEntityInSphere( pSightEnt, pev->origin, iDistance )) != NULL)
|
||||
{
|
||||
// only consider ents that can be damaged. !!!temporarily only considering other monsters and clients
|
||||
if ( pSightEnt->IsPlayer() || FBitSet ( pSightEnt->pev->flags, FL_MONSTER ) )
|
||||
{
|
||||
if ( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && pSightEnt->pev->health > 0 )
|
||||
{
|
||||
// NULL the Link pointer for each ent added to the link list. If other ents follow, the will overwrite
|
||||
// this value. If this ent happens to be the last, the list will be properly terminated.
|
||||
pPreviousEnt->m_pLink = pSightEnt;
|
||||
pSightEnt->m_pLink = NULL;
|
||||
pPreviousEnt = pSightEnt;
|
||||
|
||||
// don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
|
||||
// we see monsters other than the Enemy.
|
||||
switch ( IRelationship ( pSightEnt ) )
|
||||
{
|
||||
case R_FR:
|
||||
iSighted |= bits_COND_SEE_FEAR;
|
||||
break;
|
||||
case R_NO:
|
||||
break;
|
||||
default:
|
||||
ALERT ( at_console, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SetConditions( iSighted );
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Scheduling
|
||||
//=========================================================
|
||||
|
||||
#ifndef SCHEDULE_H
|
||||
#define SCHEDULE_H
|
||||
|
||||
#define TASKSTATUS_NEW 0 // Just started
|
||||
#define TASKSTATUS_RUNNING 1 // Running task & movement
|
||||
#define TASKSTATUS_RUNNING_MOVEMENT 2 // Just running movement
|
||||
#define TASKSTATUS_RUNNING_TASK 3 // Just running task
|
||||
#define TASKSTATUS_COMPLETE 4 // Completed, get next task
|
||||
|
||||
|
||||
//=========================================================
|
||||
// These are the schedule types
|
||||
//=========================================================
|
||||
typedef enum
|
||||
{
|
||||
SCHED_NONE = 0,
|
||||
SCHED_IDLE_STAND,
|
||||
SCHED_IDLE_WALK,
|
||||
SCHED_WAKE_ANGRY,
|
||||
SCHED_WAKE_CALLED,
|
||||
SCHED_ALERT_FACE,
|
||||
SCHED_ALERT_SMALL_FLINCH,
|
||||
SCHED_ALERT_BIG_FLINCH,
|
||||
SCHED_ALERT_STAND,
|
||||
SCHED_INVESTIGATE_SOUND,
|
||||
SCHED_COMBAT_FACE,
|
||||
SCHED_COMBAT_STAND,
|
||||
SCHED_CHASE_ENEMY,
|
||||
SCHED_CHASE_ENEMY_FAILED,
|
||||
SCHED_VICTORY_DANCE,
|
||||
SCHED_TARGET_FACE,
|
||||
SCHED_TARGET_CHASE,
|
||||
SCHED_SMALL_FLINCH,
|
||||
SCHED_TAKE_COVER_FROM_ENEMY,
|
||||
SCHED_TAKE_COVER_FROM_BEST_SOUND,
|
||||
SCHED_TAKE_COVER_FROM_ORIGIN,
|
||||
SCHED_COWER, // usually a last resort!
|
||||
SCHED_MELEE_ATTACK1,
|
||||
SCHED_MELEE_ATTACK2,
|
||||
SCHED_RANGE_ATTACK1,
|
||||
SCHED_RANGE_ATTACK2,
|
||||
SCHED_SPECIAL_ATTACK1,
|
||||
SCHED_SPECIAL_ATTACK2,
|
||||
SCHED_STANDOFF,
|
||||
SCHED_ARM_WEAPON,
|
||||
SCHED_RELOAD,
|
||||
SCHED_GUARD,
|
||||
SCHED_AMBUSH,
|
||||
SCHED_DIE,
|
||||
SCHED_WAIT_TRIGGER,
|
||||
SCHED_FOLLOW,
|
||||
SCHED_SLEEP,
|
||||
SCHED_WAKE,
|
||||
SCHED_BARNACLE_VICTIM_GRAB,
|
||||
SCHED_BARNACLE_VICTIM_CHOMP,
|
||||
SCHED_AISCRIPT,
|
||||
SCHED_FAIL,
|
||||
|
||||
LAST_COMMON_SCHEDULE // Leave this at the bottom
|
||||
} SCHEDULE_TYPE;
|
||||
|
||||
//=========================================================
|
||||
// These are the shared tasks
|
||||
//=========================================================
|
||||
typedef enum
|
||||
{
|
||||
TASK_INVALID = 0,
|
||||
TASK_WAIT,
|
||||
TASK_WAIT_FACE_ENEMY,
|
||||
TASK_WAIT_PVS,
|
||||
TASK_SUGGEST_STATE,
|
||||
TASK_WALK_TO_SCRIPT,
|
||||
TASK_RUN_TO_SCRIPT,
|
||||
TASK_MOVE_TO_TARGET_RANGE,
|
||||
TASK_GET_PATH_TO_ENEMY,
|
||||
TASK_GET_PATH_TO_ENEMY_LKP,
|
||||
TASK_GET_PATH_TO_ENEMY_CORPSE,
|
||||
TASK_GET_PATH_TO_LEADER,
|
||||
TASK_GET_PATH_TO_SPOT,
|
||||
TASK_GET_PATH_TO_TARGET,
|
||||
TASK_GET_PATH_TO_SCRIPT,
|
||||
TASK_GET_PATH_TO_HINTNODE,
|
||||
TASK_GET_PATH_TO_LASTPOSITION,
|
||||
TASK_GET_PATH_TO_BESTSOUND,
|
||||
TASK_GET_PATH_TO_BESTSCENT,
|
||||
TASK_RUN_PATH,
|
||||
TASK_WALK_PATH,
|
||||
TASK_STRAFE_PATH,
|
||||
TASK_CLEAR_MOVE_WAIT,
|
||||
TASK_STORE_LASTPOSITION,
|
||||
TASK_CLEAR_LASTPOSITION,
|
||||
TASK_PLAY_ACTIVE_IDLE,
|
||||
TASK_FIND_HINTNODE,
|
||||
TASK_CLEAR_HINTNODE,
|
||||
TASK_SMALL_FLINCH,
|
||||
TASK_FACE_IDEAL,
|
||||
TASK_FACE_ROUTE,
|
||||
TASK_FACE_ENEMY,
|
||||
TASK_FACE_HINTNODE,
|
||||
TASK_FACE_TARGET,
|
||||
TASK_FACE_LASTPOSITION,
|
||||
TASK_RANGE_ATTACK1,
|
||||
TASK_RANGE_ATTACK2,
|
||||
TASK_MELEE_ATTACK1,
|
||||
TASK_MELEE_ATTACK2,
|
||||
TASK_RELOAD,
|
||||
TASK_RANGE_ATTACK1_NOTURN,
|
||||
TASK_RANGE_ATTACK2_NOTURN,
|
||||
TASK_MELEE_ATTACK1_NOTURN,
|
||||
TASK_MELEE_ATTACK2_NOTURN,
|
||||
TASK_RELOAD_NOTURN,
|
||||
TASK_SPECIAL_ATTACK1,
|
||||
TASK_SPECIAL_ATTACK2,
|
||||
TASK_CROUCH,
|
||||
TASK_STAND,
|
||||
TASK_GUARD,
|
||||
TASK_STEP_LEFT,
|
||||
TASK_STEP_RIGHT,
|
||||
TASK_STEP_FORWARD,
|
||||
TASK_STEP_BACK,
|
||||
TASK_DODGE_LEFT,
|
||||
TASK_DODGE_RIGHT,
|
||||
TASK_SOUND_ANGRY,
|
||||
TASK_SOUND_DEATH,
|
||||
TASK_SET_ACTIVITY,
|
||||
TASK_SET_SCHEDULE,
|
||||
TASK_SET_FAIL_SCHEDULE,
|
||||
TASK_CLEAR_FAIL_SCHEDULE,
|
||||
TASK_PLAY_SEQUENCE,
|
||||
TASK_PLAY_SEQUENCE_FACE_ENEMY,
|
||||
TASK_PLAY_SEQUENCE_FACE_TARGET,
|
||||
TASK_SOUND_IDLE,
|
||||
TASK_SOUND_WAKE,
|
||||
TASK_SOUND_PAIN,
|
||||
TASK_SOUND_DIE,
|
||||
TASK_FIND_COVER_FROM_BEST_SOUND,// tries lateral cover first, then node cover
|
||||
TASK_FIND_COVER_FROM_ENEMY,// tries lateral cover first, then node cover
|
||||
TASK_FIND_LATERAL_COVER_FROM_ENEMY,
|
||||
TASK_FIND_NODE_COVER_FROM_ENEMY,
|
||||
TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY,// data for this one is the MAXIMUM acceptable distance to the cover.
|
||||
TASK_FIND_FAR_NODE_COVER_FROM_ENEMY,// data for this one is there MINIMUM aceptable distance to the cover.
|
||||
TASK_FIND_COVER_FROM_ORIGIN,
|
||||
TASK_EAT,
|
||||
TASK_DIE,
|
||||
TASK_WAIT_FOR_SCRIPT,
|
||||
TASK_PLAY_SCRIPT,
|
||||
TASK_ENABLE_SCRIPT,
|
||||
TASK_PLANT_ON_SCRIPT,
|
||||
TASK_FACE_SCRIPT,
|
||||
TASK_END_SCRIPT, //LRC
|
||||
TASK_WAIT_RANDOM,
|
||||
TASK_WAIT_INDEFINITE,
|
||||
TASK_STOP_MOVING,
|
||||
TASK_TURN_LEFT,
|
||||
TASK_TURN_RIGHT,
|
||||
TASK_REMEMBER,
|
||||
TASK_FORGET,
|
||||
TASK_WAIT_FOR_MOVEMENT, // wait until MovementIsComplete()
|
||||
LAST_COMMON_TASK, // LEAVE THIS AT THE BOTTOM!! (sjb)
|
||||
} SHARED_TASKS;
|
||||
|
||||
|
||||
// These go in the flData member of the TASK_WALK_TO_TARGET, TASK_RUN_TO_TARGET
|
||||
enum
|
||||
{
|
||||
TARGET_MOVE_NORMAL = 0,
|
||||
TARGET_MOVE_SCRIPTED = 1,
|
||||
};
|
||||
|
||||
|
||||
// A goal should be used for a task that requires several schedules to complete.
|
||||
// The goal index should indicate which schedule (ordinally) the monster is running.
|
||||
// That way, when tasks fail, the AI can make decisions based on the context of the
|
||||
// current goal and sequence rather than just the current schedule.
|
||||
enum
|
||||
{
|
||||
GOAL_ATTACK_ENEMY,
|
||||
GOAL_MOVE,
|
||||
GOAL_TAKE_COVER,
|
||||
GOAL_MOVE_TARGET,
|
||||
GOAL_EAT,
|
||||
};
|
||||
|
||||
// an array of tasks is a task list
|
||||
// an array of schedules is a schedule list
|
||||
struct Task_t
|
||||
{
|
||||
|
||||
int iTask;
|
||||
float flData;
|
||||
};
|
||||
|
||||
struct Schedule_t
|
||||
{
|
||||
|
||||
Task_t *pTasklist;
|
||||
int cTasks;
|
||||
int iInterruptMask;// a bit mask of conditions that can interrupt this schedule
|
||||
|
||||
// a more specific mask that indicates which TYPES of sounds will interrupt the schedule in the
|
||||
// event that the schedule is broken by COND_HEAR_SOUND
|
||||
int iSoundMask;
|
||||
const char *pName;
|
||||
};
|
||||
|
||||
// an array of waypoints makes up the monster's route.
|
||||
// !!!LATER- this declaration doesn't belong in this file.
|
||||
struct WayPoint_t
|
||||
{
|
||||
Vector vecLocation;
|
||||
int iType;
|
||||
};
|
||||
|
||||
// these MoveFlag values are assigned to a WayPoint's TYPE in order to demonstrate the
|
||||
// type of movement the monster should use to get there.
|
||||
#define bits_MF_TO_TARGETENT ( 1 << 0 ) // local move to targetent.
|
||||
#define bits_MF_TO_ENEMY ( 1 << 1 ) // local move to enemy
|
||||
#define bits_MF_TO_COVER ( 1 << 2 ) // local move to a hiding place
|
||||
#define bits_MF_TO_DETOUR ( 1 << 3 ) // local move to detour point.
|
||||
#define bits_MF_TO_PATHCORNER ( 1 << 4 ) // local move to a path corner
|
||||
#define bits_MF_TO_NODE ( 1 << 5 ) // local move to a node
|
||||
#define bits_MF_TO_LOCATION ( 1 << 6 ) // local move to an arbitrary point
|
||||
#define bits_MF_IS_GOAL ( 1 << 7 ) // this waypoint is the goal of the whole move.
|
||||
#define bits_MF_DONT_SIMPLIFY ( 1 << 8 ) // Don't let the route code simplify this waypoint
|
||||
|
||||
// If you define any flags that aren't _TO_ flags, add them here so we can mask
|
||||
// them off when doing compares.
|
||||
#define bits_MF_NOT_TO_MASK (bits_MF_IS_GOAL | bits_MF_DONT_SIMPLIFY)
|
||||
|
||||
#define MOVEGOAL_NONE (0)
|
||||
#define MOVEGOAL_TARGETENT (bits_MF_TO_TARGETENT)
|
||||
#define MOVEGOAL_ENEMY (bits_MF_TO_ENEMY)
|
||||
#define MOVEGOAL_PATHCORNER (bits_MF_TO_PATHCORNER)
|
||||
#define MOVEGOAL_LOCATION (bits_MF_TO_LOCATION)
|
||||
#define MOVEGOAL_NODE (bits_MF_TO_NODE)
|
||||
|
||||
// these bits represent conditions that may befall the monster, of which some are allowed
|
||||
// to interrupt certain schedules.
|
||||
#define bits_COND_NO_AMMO_LOADED ( 1 << 0 ) // weapon needs to be reloaded!
|
||||
#define bits_COND_SEE_HATE ( 1 << 1 ) // see something that you hate
|
||||
#define bits_COND_SEE_FEAR ( 1 << 2 ) // see something that you are afraid of
|
||||
#define bits_COND_SEE_DISLIKE ( 1 << 3 ) // see something that you dislike
|
||||
#define bits_COND_SEE_ENEMY ( 1 << 4 ) // target entity is in full view.
|
||||
#define bits_COND_ENEMY_OCCLUDED ( 1 << 5 ) // target entity occluded by the world
|
||||
#define bits_COND_SMELL_FOOD ( 1 << 6 )
|
||||
#define bits_COND_ENEMY_TOOFAR ( 1 << 7 )
|
||||
#define bits_COND_LIGHT_DAMAGE ( 1 << 8 ) // hurt a little
|
||||
#define bits_COND_HEAVY_DAMAGE ( 1 << 9 ) // hurt a lot
|
||||
#define bits_COND_CAN_RANGE_ATTACK1 ( 1 << 10)
|
||||
#define bits_COND_CAN_MELEE_ATTACK1 ( 1 << 11)
|
||||
#define bits_COND_CAN_RANGE_ATTACK2 ( 1 << 12)
|
||||
#define bits_COND_CAN_MELEE_ATTACK2 ( 1 << 13)
|
||||
// #define bits_COND_CAN_RANGE_ATTACK3 ( 1 << 14)
|
||||
#define bits_COND_PROVOKED ( 1 << 15)
|
||||
#define bits_COND_NEW_ENEMY ( 1 << 16)
|
||||
#define bits_COND_HEAR_SOUND ( 1 << 17) // there is an interesting sound
|
||||
#define bits_COND_SMELL ( 1 << 18) // there is an interesting scent
|
||||
#define bits_COND_ENEMY_FACING_ME ( 1 << 19) // enemy is facing me
|
||||
#define bits_COND_ENEMY_DEAD ( 1 << 20) // enemy was killed. If you get this in combat, try to find another enemy. If you get it in alert, victory dance.
|
||||
#define bits_COND_SEE_CLIENT ( 1 << 21) // see a client
|
||||
#define bits_COND_SEE_NEMESIS ( 1 << 22) // see my nemesis
|
||||
|
||||
#define bits_COND_SPECIAL1 ( 1 << 28) // Defined by individual monster
|
||||
#define bits_COND_SPECIAL2 ( 1 << 29) // Defined by individual monster
|
||||
|
||||
#define bits_COND_TASK_FAILED ( 1 << 30)
|
||||
#define bits_COND_SCHEDULE_DONE ( 1 << 31)
|
||||
|
||||
|
||||
#define bits_COND_ALL_SPECIAL (bits_COND_SPECIAL1 | bits_COND_SPECIAL2)
|
||||
|
||||
#define bits_COND_CAN_ATTACK (bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2)
|
||||
|
||||
#endif // SCHEDULE_H
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1999, 2000 Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#ifndef SCRIPTED_H
|
||||
#define SCRIPTED_H
|
||||
|
||||
#ifndef SCRIPTEVENT_H
|
||||
#include "scriptevent.h"
|
||||
#endif
|
||||
|
||||
#define SF_SCRIPT_WAITTILLSEEN 1
|
||||
#define SF_SCRIPT_EXITAGITATED 2
|
||||
#define SF_SCRIPT_REPEATABLE 4
|
||||
#define SF_SCRIPT_LEAVECORPSE 8
|
||||
//#define SF_SCRIPT_INTERPOLATE 16 // don't use, old bug
|
||||
#define SF_SCRIPT_NOINTERRUPT 32
|
||||
#define SF_SCRIPT_OVERRIDESTATE 64
|
||||
#define SF_SCRIPT_NOSCRIPTMOVEMENT 128
|
||||
#define SF_SCRIPT_STAYDEAD 256 // LRC- signifies that the animation kills the monster
|
||||
// (needed because the monster animations don't use AnimEvent 1000 properly)
|
||||
|
||||
#define SCRIPT_BREAK_CONDITIONS (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE)
|
||||
|
||||
//LRC - rearranged into flags
|
||||
#define SS_INTERRUPT_IDLE 0x0
|
||||
#define SS_INTERRUPT_ALERT 0x1
|
||||
#define SS_INTERRUPT_ANYSTATE 0x2
|
||||
#define SS_INTERRUPT_SCRIPTS 0x4
|
||||
|
||||
// when a monster finishes an AI scripted sequence, we can choose
|
||||
// a schedule to place them in. These defines are the aliases to
|
||||
// resolve worldcraft input to real schedules (sjb)
|
||||
#define SCRIPT_FINISHSCHED_DEFAULT 0
|
||||
#define SCRIPT_FINISHSCHED_AMBUSH 1
|
||||
|
||||
class CCineMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
virtual void KeyValue( KeyValueData *pkvd );
|
||||
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
virtual void Blocked( CBaseEntity *pOther );
|
||||
virtual void Touch( CBaseEntity *pOther );
|
||||
virtual int ObjectCaps( void ) { return (CBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
|
||||
virtual void Activate( void );
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
//LRC: states for script entities
|
||||
virtual STATE GetState( void ) { return m_iState; };
|
||||
STATE m_iState;
|
||||
|
||||
// void EXPORT CineSpawnThink( void );
|
||||
void EXPORT CineThink( void );
|
||||
void EXPORT InitIdleThink( void ); //LRC
|
||||
void Pain( void );
|
||||
void Die( void );
|
||||
void DelayStart( int state );
|
||||
CBaseMonster* FindEntity( const char* sName, CBaseEntity *pActivator );
|
||||
virtual void PossessEntity( void );
|
||||
|
||||
inline BOOL IsAction( void ) { return FClassnameIs(pev,"scripted_action"); }; //LRC
|
||||
|
||||
//LRC: Should the monster do a precise attack for this scripted_action?
|
||||
// (Do a precise attack if we'll be turning to face the target, but we haven't just walked to the target.)
|
||||
BOOL PreciseAttack( void )
|
||||
{
|
||||
// if (m_fTurnType != 1) { ALERT(at_console,"preciseattack fails check 1\n"); return FALSE; }
|
||||
// if (m_fMoveTo == 0) { ALERT(at_console,"preciseattack fails check 2\n"); return FALSE; }
|
||||
// if (m_fMoveTo != 5 && m_iszAttack == 0) { ALERT(at_console,"preciseattack fails check 3\n"); return FALSE; }
|
||||
// ALERT(at_console,"preciseattack passes!\n");
|
||||
// return TRUE;
|
||||
return m_fTurnType == 1 && ( m_fMoveTo == 5 || (m_fMoveTo != 0 && !FStrEq(STRING(m_iszAttack), STRING(m_iszMoveTarget)) ));
|
||||
};
|
||||
|
||||
void ReleaseEntity( CBaseMonster *pEntity );
|
||||
void CancelScript( void );
|
||||
virtual BOOL StartSequence( CBaseMonster *pTarget, int iszSeq, BOOL completeOnEmpty );
|
||||
void SequenceDone ( CBaseMonster *pMonster );
|
||||
virtual void FixScriptMonsterSchedule( CBaseMonster *pMonster );
|
||||
BOOL CanInterrupt( void );
|
||||
void AllowInterrupt( BOOL fAllow );
|
||||
int IgnoreConditions( void );
|
||||
|
||||
int m_iszIdle; // string index for idle animation
|
||||
int m_iszPlay; // string index for scripted animation
|
||||
int m_iszEntity; // entity that is wanted for this script
|
||||
int m_iszAttack; // entity to attack
|
||||
int m_iszMoveTarget; // entity to move to
|
||||
int m_iszFireOnBegin; // entity to fire when the sequence _starts_.
|
||||
int m_fMoveTo;
|
||||
int m_fTurnType;
|
||||
int m_fAction;
|
||||
int m_iFinishSchedule;
|
||||
float m_flRadius; // range to search
|
||||
//LRC- this does nothing!! float m_flRepeat; // repeat rate
|
||||
int m_iRepeats; //LRC - number of times to repeat the animation
|
||||
int m_iRepeatsLeft; //LRC
|
||||
float m_fRepeatFrame; //LRC
|
||||
int m_iPriority; //LRC
|
||||
|
||||
int m_iDelay;
|
||||
float m_startTime;
|
||||
|
||||
int m_saved_movetype;
|
||||
int m_saved_solid;
|
||||
int m_saved_effects;
|
||||
// Vector m_vecOrigOrigin;
|
||||
BOOL m_interruptable;
|
||||
};
|
||||
|
||||
//LRC - removed CCineAI, obsolete
|
||||
|
||||
#endif //SCRIPTED_H
|
|
@ -0,0 +1,29 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Use, distribution, and modification of this source code and/or resulting
|
||||
* object code is restricted to non-commercial enhancements to products from
|
||||
* Valve LLC. All other use, distribution, or modification is prohibited
|
||||
* without written permission from Valve LLC.
|
||||
*
|
||||
****/
|
||||
#ifndef SCRIPTEVENT_H
|
||||
#define SCRIPTEVENT_H
|
||||
|
||||
#define SCRIPT_EVENT_DEAD 1000 // character is now dead
|
||||
#define SCRIPT_EVENT_NOINTERRUPT 1001 // does not allow interrupt
|
||||
#define SCRIPT_EVENT_CANINTERRUPT 1002 // will allow interrupt
|
||||
#define SCRIPT_EVENT_FIREEVENT 1003 // event now fires
|
||||
#define SCRIPT_EVENT_SOUND 1004 // Play named wave file (on CHAN_BODY)
|
||||
#define SCRIPT_EVENT_SENTENCE 1005 // Play named sentence
|
||||
#define SCRIPT_EVENT_INAIR 1006 // Leave the character in air at the end of the sequence (don't find the floor)
|
||||
#define SCRIPT_EVENT_ENDANIMATION 1007 // Set the animation by name after the sequence completes
|
||||
#define SCRIPT_EVENT_SOUND_VOICE 1008 // Play named wave file (on CHAN_VOICE)
|
||||
#define SCRIPT_EVENT_SENTENCE_RND1 1009 // Play sentence group 25% of the time
|
||||
#define SCRIPT_EVENT_NOT_DEAD 1010 // Bring back to life (for life/death sequences)
|
||||
#endif //SCRIPTEVENT_H
|
|
@ -0,0 +1,20 @@
|
|||
//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: New version of the slider bar
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
//=========================================================
|
||||
// squad.h
|
||||
//=========================================================
|
||||
|
||||
// these are special group roles that are assigned to members when the group is formed.
|
||||
// the reason these are explicitly assigned and tasks like throwing grenades to flush out
|
||||
// enemies is that it's bad to have two members trying to flank left at the same time, but
|
||||
// ok to have two throwing grenades at the same time. When a squad member cannot attack the
|
||||
// enemy, it will choose to execute its special role.
|
||||
#define bits_SQUAD_FLANK_LEFT ( 1 << 0 )
|
||||
#define bits_SQUAD_FLANK_RIGHT ( 1 << 1 )
|
||||
#define bits_SQUAD_ADVANCE ( 1 << 2 )
|
||||
#define bits_SQUAD_FLUSH_ATTACK ( 1 << 3 )
|
|
@ -0,0 +1,642 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Squadmonster functions
|
||||
//=========================================================
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "nodes.h"
|
||||
#include "monsters.h"
|
||||
#include "animation.h"
|
||||
#include "saverestore.h"
|
||||
#include "squadmonster.h"
|
||||
#include "plane.h"
|
||||
|
||||
//=========================================================
|
||||
// Save/Restore
|
||||
//=========================================================
|
||||
TYPEDESCRIPTION CSquadMonster::m_SaveData[] =
|
||||
{
|
||||
DEFINE_FIELD( CSquadMonster, m_hSquadLeader, FIELD_EHANDLE ),
|
||||
DEFINE_ARRAY( CSquadMonster, m_hSquadMember, FIELD_EHANDLE, MAX_SQUAD_MEMBERS - 1 ),
|
||||
|
||||
DEFINE_FIELD( CSquadMonster, m_fEnemyEluded, FIELD_BOOLEAN ),
|
||||
DEFINE_FIELD( CSquadMonster, m_flLastEnemySightTime, FIELD_TIME ),
|
||||
|
||||
DEFINE_FIELD( CSquadMonster, m_iMySlot, FIELD_INTEGER ),
|
||||
|
||||
|
||||
};
|
||||
|
||||
IMPLEMENT_SAVERESTORE( CSquadMonster, CBaseMonster );
|
||||
|
||||
|
||||
//=========================================================
|
||||
// OccupySlot - if any slots of the passed slots are
|
||||
// available, the monster will be assigned to one.
|
||||
//=========================================================
|
||||
BOOL CSquadMonster :: OccupySlot( int iDesiredSlots )
|
||||
{
|
||||
int i;
|
||||
int iMask;
|
||||
int iSquadSlots;
|
||||
|
||||
if ( !InSquad() )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ( SquadEnemySplit() )
|
||||
{
|
||||
// if the squad members aren't all fighting the same enemy, slots are disabled
|
||||
// so that a squad member doesn't get stranded unable to engage his enemy because
|
||||
// all of the attack slots are taken by squad members fighting other enemies.
|
||||
m_iMySlot = bits_SLOT_SQUAD_SPLIT;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
CSquadMonster *pSquadLeader = MySquadLeader();
|
||||
|
||||
if ( !( iDesiredSlots ^ pSquadLeader->m_afSquadSlots ) )
|
||||
{
|
||||
// none of the desired slots are available.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
iSquadSlots = pSquadLeader->m_afSquadSlots;
|
||||
|
||||
for ( i = 0; i < NUM_SLOTS; i++ )
|
||||
{
|
||||
iMask = 1<<i;
|
||||
if ( iDesiredSlots & iMask ) // am I looking for this bit?
|
||||
{
|
||||
if ( !(iSquadSlots & iMask) ) // Is it already taken?
|
||||
{
|
||||
// No, use this bit
|
||||
pSquadLeader->m_afSquadSlots |= iMask;
|
||||
m_iMySlot = iMask;
|
||||
// ALERT ( at_aiconsole, "Took slot %d - %d\n", i, m_hSquadLeader->m_afSquadSlots );
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// VacateSlot
|
||||
//=========================================================
|
||||
void CSquadMonster :: VacateSlot()
|
||||
{
|
||||
if ( m_iMySlot != bits_NO_SLOT && InSquad() )
|
||||
{
|
||||
// ALERT ( at_aiconsole, "Vacated Slot %d - %d\n", m_iMySlot, m_hSquadLeader->m_afSquadSlots );
|
||||
MySquadLeader()->m_afSquadSlots &= ~m_iMySlot;
|
||||
m_iMySlot = bits_NO_SLOT;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// ScheduleChange
|
||||
//=========================================================
|
||||
void CSquadMonster :: ScheduleChange ( void )
|
||||
{
|
||||
VacateSlot();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Killed
|
||||
//=========================================================
|
||||
void CSquadMonster :: Killed( entvars_t *pevAttacker, int iGib )
|
||||
{
|
||||
VacateSlot();
|
||||
|
||||
if ( InSquad() )
|
||||
{
|
||||
MySquadLeader()->SquadRemove( this );
|
||||
}
|
||||
|
||||
CBaseMonster :: Killed ( pevAttacker, iGib );
|
||||
}
|
||||
|
||||
// These functions are still awaiting conversion to CSquadMonster
|
||||
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadRemove(), remove pRemove from my squad.
|
||||
// If I am pRemove, promote m_pSquadNext to leader
|
||||
//
|
||||
//=========================================================
|
||||
void CSquadMonster :: SquadRemove( CSquadMonster *pRemove )
|
||||
{
|
||||
ASSERT( pRemove!=NULL );
|
||||
ASSERT( this->IsLeader() );
|
||||
ASSERT( pRemove->m_hSquadLeader == this );
|
||||
|
||||
// If I'm the leader, get rid of my squad
|
||||
if (pRemove == MySquadLeader())
|
||||
{
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS-1;i++)
|
||||
{
|
||||
CSquadMonster *pMember = MySquadMember(i);
|
||||
if (pMember)
|
||||
{
|
||||
pMember->m_hSquadLeader = NULL;
|
||||
m_hSquadMember[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CSquadMonster *pSquadLeader = MySquadLeader();
|
||||
if (pSquadLeader)
|
||||
{
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS-1;i++)
|
||||
{
|
||||
if (pSquadLeader->m_hSquadMember[i] == this)
|
||||
{
|
||||
pSquadLeader->m_hSquadMember[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pRemove->m_hSquadLeader = NULL;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadAdd(), add pAdd to my squad
|
||||
//
|
||||
//=========================================================
|
||||
BOOL CSquadMonster :: SquadAdd( CSquadMonster *pAdd )
|
||||
{
|
||||
ASSERT( pAdd!=NULL );
|
||||
ASSERT( !pAdd->InSquad() );
|
||||
ASSERT( this->IsLeader() );
|
||||
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS-1; i++)
|
||||
{
|
||||
if (m_hSquadMember[i] == NULL)
|
||||
{
|
||||
m_hSquadMember[i] = pAdd;
|
||||
pAdd->m_hSquadLeader = this;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
// should complain here
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadPasteEnemyInfo - called by squad members that have
|
||||
// current info on the enemy so that it can be stored for
|
||||
// members who don't have current info.
|
||||
//
|
||||
//=========================================================
|
||||
void CSquadMonster :: SquadPasteEnemyInfo ( void )
|
||||
{
|
||||
CSquadMonster *pSquadLeader = MySquadLeader( );
|
||||
if (pSquadLeader)
|
||||
pSquadLeader->m_vecEnemyLKP = m_vecEnemyLKP;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadCopyEnemyInfo - called by squad members who don't
|
||||
// have current info on the enemy. Reads from the same fields
|
||||
// in the leader's data that other squad members write to,
|
||||
// so the most recent data is always available here.
|
||||
//
|
||||
//=========================================================
|
||||
void CSquadMonster :: SquadCopyEnemyInfo ( void )
|
||||
{
|
||||
CSquadMonster *pSquadLeader = MySquadLeader( );
|
||||
if (pSquadLeader)
|
||||
m_vecEnemyLKP = pSquadLeader->m_vecEnemyLKP;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadMakeEnemy - makes everyone in the squad angry at
|
||||
// the same entity.
|
||||
//
|
||||
//=========================================================
|
||||
void CSquadMonster :: SquadMakeEnemy ( CBaseEntity *pEnemy )
|
||||
{
|
||||
if (!InSquad())
|
||||
return;
|
||||
|
||||
if ( !pEnemy )
|
||||
{
|
||||
ALERT ( at_console, "ERROR: SquadMakeEnemy() - pEnemy is NULL!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
CSquadMonster *pSquadLeader = MySquadLeader( );
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS; i++)
|
||||
{
|
||||
CSquadMonster *pMember = pSquadLeader->MySquadMember(i);
|
||||
if (pMember)
|
||||
{
|
||||
// reset members who aren't activly engaged in fighting
|
||||
if (pMember->m_hEnemy != pEnemy && !pMember->HasConditions( bits_COND_SEE_ENEMY))
|
||||
{
|
||||
if ( pMember->m_hEnemy != NULL)
|
||||
{
|
||||
// remember their current enemy
|
||||
pMember->PushEnemy( pMember->m_hEnemy, pMember->m_vecEnemyLKP );
|
||||
}
|
||||
// give them a new enemy
|
||||
pMember->m_hEnemy = pEnemy;
|
||||
pMember->m_vecEnemyLKP = pEnemy->pev->origin;
|
||||
pMember->SetConditions ( bits_COND_NEW_ENEMY );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadCount(), return the number of members of this squad
|
||||
// callable from leaders & followers
|
||||
//
|
||||
//=========================================================
|
||||
int CSquadMonster :: SquadCount( void )
|
||||
{
|
||||
if (!InSquad())
|
||||
return 0;
|
||||
|
||||
CSquadMonster *pSquadLeader = MySquadLeader();
|
||||
int squadCount = 0;
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS; i++)
|
||||
{
|
||||
if (pSquadLeader->MySquadMember(i) != NULL)
|
||||
squadCount++;
|
||||
}
|
||||
|
||||
return squadCount;
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
//
|
||||
// SquadRecruit(), get some monsters of my classification and
|
||||
// link them as a group. returns the group size
|
||||
//
|
||||
//=========================================================
|
||||
int CSquadMonster :: SquadRecruit( int searchRadius, int maxMembers )
|
||||
{
|
||||
int squadCount;
|
||||
int iMyClass = Classify();// cache this monster's class
|
||||
|
||||
|
||||
// Don't recruit if I'm already in a group
|
||||
if ( InSquad() )
|
||||
return 0;
|
||||
|
||||
if ( maxMembers < 2 )
|
||||
return 0;
|
||||
|
||||
// I am my own leader
|
||||
m_hSquadLeader = this;
|
||||
squadCount = 1;
|
||||
|
||||
CBaseEntity *pEntity = NULL;
|
||||
|
||||
if ( !FStringNull( pev->netname ) )
|
||||
{
|
||||
// I have a netname, so unconditionally recruit everyone else with that name.
|
||||
pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ) );
|
||||
while ( pEntity )
|
||||
{
|
||||
CSquadMonster *pRecruit = pEntity->MySquadMonsterPointer();
|
||||
|
||||
if ( pRecruit )
|
||||
{
|
||||
if ( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass && pRecruit != this )
|
||||
{
|
||||
// minimum protection here against user error.in worldcraft.
|
||||
if (!SquadAdd( pRecruit ))
|
||||
break;
|
||||
squadCount++;
|
||||
}
|
||||
}
|
||||
|
||||
pEntity = UTIL_FindEntityByString( pEntity, "netname", STRING( pev->netname ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, searchRadius )) != NULL)
|
||||
{
|
||||
CSquadMonster *pRecruit = pEntity->MySquadMonsterPointer( );
|
||||
|
||||
if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine )
|
||||
{
|
||||
// Can we recruit this guy?
|
||||
if ( !pRecruit->InSquad() && pRecruit->Classify() == iMyClass &&
|
||||
( (iMyClass != CLASS_ALIEN_MONSTER) || FStrEq(STRING(pev->classname), STRING(pRecruit->pev->classname))) &&
|
||||
FStringNull( pRecruit->pev->netname ) )
|
||||
{
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine( pev->origin + pev->view_ofs, pRecruit->pev->origin + pev->view_ofs, ignore_monsters, pRecruit->edict(), &tr );// try to hit recruit with a traceline.
|
||||
if ( tr.flFraction == 1.0 )
|
||||
{
|
||||
if (!SquadAdd( pRecruit ))
|
||||
break;
|
||||
|
||||
squadCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no single member squads
|
||||
if (squadCount == 1)
|
||||
{
|
||||
m_hSquadLeader = NULL;
|
||||
}
|
||||
|
||||
return squadCount;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// CheckEnemy
|
||||
//=========================================================
|
||||
int CSquadMonster :: CheckEnemy ( CBaseEntity *pEnemy )
|
||||
{
|
||||
int iUpdatedLKP;
|
||||
|
||||
iUpdatedLKP = CBaseMonster :: CheckEnemy ( m_hEnemy );
|
||||
|
||||
// communicate with squad members about the enemy IF this individual has the same enemy as the squad leader.
|
||||
if ( InSquad() && (CBaseEntity *)m_hEnemy == MySquadLeader()->m_hEnemy )
|
||||
{
|
||||
if ( iUpdatedLKP )
|
||||
{
|
||||
// have new enemy information, so paste to the squad.
|
||||
SquadPasteEnemyInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
// enemy unseen, copy from the squad knowledge.
|
||||
SquadCopyEnemyInfo();
|
||||
}
|
||||
}
|
||||
|
||||
return iUpdatedLKP;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// StartMonster
|
||||
//=========================================================
|
||||
void CSquadMonster :: StartMonster( void )
|
||||
{
|
||||
CBaseMonster :: StartMonster();
|
||||
|
||||
if ( ( m_afCapability & bits_CAP_SQUAD ) && !InSquad() )
|
||||
{
|
||||
if ( !FStringNull( pev->netname ) )
|
||||
{
|
||||
// if I have a groupname, I can only recruit if I'm flagged as leader
|
||||
if ( !( pev->spawnflags & SF_SQUADMONSTER_LEADER ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// try to form squads now.
|
||||
int iSquadSize = SquadRecruit( 1024, 4 );
|
||||
|
||||
if ( iSquadSize )
|
||||
{
|
||||
ALERT ( at_aiconsole, "Squad of %d %s formed\n", iSquadSize, STRING( pev->classname ) );
|
||||
}
|
||||
|
||||
if ( IsLeader() && FClassnameIs ( pev, "monster_human_grunt" ) )
|
||||
{
|
||||
SetBodygroup( 1, 1 ); // UNDONE: truly ugly hack
|
||||
pev->skin = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CSquadMonster :: NoFriendlyFire( void )
|
||||
{
|
||||
return NoFriendlyFire( FALSE ); //default: don't like the player
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// NoFriendlyFire - checks for possibility of friendly fire
|
||||
//
|
||||
// Builds a large box in front of the grunt and checks to see
|
||||
// if any squad members are in that box.
|
||||
//
|
||||
// Can now, also, check whether the player is in the box. LRC
|
||||
//=========================================================
|
||||
BOOL CSquadMonster :: NoFriendlyFire( BOOL playerAlly )
|
||||
{
|
||||
if ( !playerAlly && !InSquad() )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VPlane backPlane;
|
||||
VPlane leftPlane;
|
||||
VPlane rightPlane;
|
||||
|
||||
Vector vecLeftSide;
|
||||
Vector vecRightSide;
|
||||
Vector v_left;
|
||||
|
||||
//!!!BUGBUG - to fix this, the planes must be aligned to where the monster will be firing its gun, not the direction it is facing!!!
|
||||
|
||||
if ( m_hEnemy != NULL )
|
||||
{
|
||||
UTIL_MakeVectors ( UTIL_VecToAngles( m_hEnemy->Center() - pev->origin ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there's no enemy, pretend there's a friendly in the way, so the grunt won't shoot.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//UTIL_MakeVectors ( pev->angles );
|
||||
|
||||
vecLeftSide = pev->origin - ( gpGlobals->v_right * ( pev->size.x * 1.5 ) );
|
||||
vecRightSide = pev->origin + ( gpGlobals->v_right * ( pev->size.x * 1.5 ) );
|
||||
v_left = gpGlobals->v_right * -1;
|
||||
|
||||
leftPlane.Init ( gpGlobals->v_right, vecLeftSide );
|
||||
rightPlane.Init ( v_left, vecRightSide );
|
||||
backPlane.Init ( gpGlobals->v_forward, pev->origin );
|
||||
|
||||
/*
|
||||
ALERT ( at_console, "LeftPlane: %f %f %f : %f\n", leftPlane.m_vecNormal.x, leftPlane.m_vecNormal.y, leftPlane.m_vecNormal.z, leftPlane.m_flDist );
|
||||
ALERT ( at_console, "RightPlane: %f %f %f : %f\n", rightPlane.m_vecNormal.x, rightPlane.m_vecNormal.y, rightPlane.m_vecNormal.z, rightPlane.m_flDist );
|
||||
ALERT ( at_console, "BackPlane: %f %f %f : %f\n", backPlane.m_vecNormal.x, backPlane.m_vecNormal.y, backPlane.m_vecNormal.z, backPlane.m_flDist );
|
||||
*/
|
||||
|
||||
CSquadMonster *pSquadLeader = MySquadLeader();
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS; i++)
|
||||
{
|
||||
CSquadMonster *pMember = pSquadLeader->MySquadMember(i);
|
||||
if (pMember && pMember != this)
|
||||
{
|
||||
|
||||
if ( backPlane.GetPointSide ( pMember->pev->origin ) &&
|
||||
leftPlane.GetPointSide ( pMember->pev->origin ) &&
|
||||
rightPlane.GetPointSide ( pMember->pev->origin) )
|
||||
{
|
||||
// this guy is in the check volume! Don't shoot!
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (playerAlly)
|
||||
{
|
||||
edict_t *pentPlayer = FIND_CLIENT_IN_PVS( edict() );
|
||||
if (!FNullEnt(pentPlayer) &&
|
||||
backPlane.GetPointSide ( pentPlayer->v.origin ) &&
|
||||
leftPlane.GetPointSide ( pentPlayer->v.origin ) &&
|
||||
rightPlane.GetPointSide ( pentPlayer->v.origin ) )
|
||||
{
|
||||
// the player is in the check volume! Don't shoot!
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// GetIdealState - surveys the Conditions information available
|
||||
// and finds the best new state for a monster.
|
||||
//=========================================================
|
||||
MONSTERSTATE CSquadMonster :: GetIdealState ( void )
|
||||
{
|
||||
int iConditions;
|
||||
|
||||
iConditions = IScheduleFlags();
|
||||
|
||||
// If no schedule conditions, the new ideal state is probably the reason we're in here.
|
||||
switch ( m_MonsterState )
|
||||
{
|
||||
case MONSTERSTATE_IDLE:
|
||||
case MONSTERSTATE_ALERT:
|
||||
if ( HasConditions ( bits_COND_NEW_ENEMY ) && InSquad() )
|
||||
{
|
||||
SquadMakeEnemy ( m_hEnemy );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return CBaseMonster :: GetIdealState();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FValidateCover - determines whether or not the chosen
|
||||
// cover location is a good one to move to. (currently based
|
||||
// on proximity to others in the squad)
|
||||
//=========================================================
|
||||
BOOL CSquadMonster :: FValidateCover ( const Vector &vecCoverLocation )
|
||||
{
|
||||
if ( !InSquad() )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (SquadMemberInRange( vecCoverLocation, 128 ))
|
||||
{
|
||||
// another squad member is too close to this piece of cover.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SquadEnemySplit- returns TRUE if not all squad members
|
||||
// are fighting the same enemy.
|
||||
//=========================================================
|
||||
BOOL CSquadMonster :: SquadEnemySplit ( void )
|
||||
{
|
||||
if (!InSquad())
|
||||
return FALSE;
|
||||
|
||||
CSquadMonster *pSquadLeader = MySquadLeader();
|
||||
CBaseEntity *pEnemy = pSquadLeader->m_hEnemy;
|
||||
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS; i++)
|
||||
{
|
||||
CSquadMonster *pMember = pSquadLeader->MySquadMember(i);
|
||||
if (pMember != NULL && pMember->m_hEnemy != NULL && pMember->m_hEnemy != pEnemy)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// FValidateCover - determines whether or not the chosen
|
||||
// cover location is a good one to move to. (currently based
|
||||
// on proximity to others in the squad)
|
||||
//=========================================================
|
||||
BOOL CSquadMonster :: SquadMemberInRange ( const Vector &vecLocation, float flDist )
|
||||
{
|
||||
if (!InSquad())
|
||||
return FALSE;
|
||||
|
||||
CSquadMonster *pSquadLeader = MySquadLeader();
|
||||
|
||||
for (int i = 0; i < MAX_SQUAD_MEMBERS; i++)
|
||||
{
|
||||
CSquadMonster *pSquadMember = pSquadLeader->MySquadMember(i);
|
||||
if (pSquadMember && (vecLocation - pSquadMember->pev->origin ).Length2D() <= flDist)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
extern Schedule_t slChaseEnemyFailed[];
|
||||
|
||||
Schedule_t *CSquadMonster::GetScheduleOfType( int iType )
|
||||
{
|
||||
switch ( iType )
|
||||
{
|
||||
|
||||
case SCHED_CHASE_ENEMY_FAILED:
|
||||
{
|
||||
return &slChaseEnemyFailed[ 0 ];
|
||||
}
|
||||
|
||||
default:
|
||||
return CBaseMonster::GetScheduleOfType( iType );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// CSquadMonster - all the extra data for monsters that
|
||||
// form squads.
|
||||
//=========================================================
|
||||
|
||||
#define SF_SQUADMONSTER_LEADER 32
|
||||
|
||||
|
||||
#define bits_NO_SLOT 0
|
||||
|
||||
// HUMAN GRUNT SLOTS
|
||||
#define bits_SLOT_HGRUNT_ENGAGE1 ( 1 << 0 )
|
||||
#define bits_SLOT_HGRUNT_ENGAGE2 ( 1 << 1 )
|
||||
#define bits_SLOTS_HGRUNT_ENGAGE ( bits_SLOT_HGRUNT_ENGAGE1 | bits_SLOT_HGRUNT_ENGAGE2 )
|
||||
|
||||
#define bits_SLOT_HGRUNT_GRENADE1 ( 1 << 2 )
|
||||
#define bits_SLOT_HGRUNT_GRENADE2 ( 1 << 3 )
|
||||
#define bits_SLOTS_HGRUNT_GRENADE ( bits_SLOT_HGRUNT_GRENADE1 | bits_SLOT_HGRUNT_GRENADE2 )
|
||||
|
||||
// ALIEN GRUNT SLOTS
|
||||
#define bits_SLOT_AGRUNT_HORNET1 ( 1 << 4 )
|
||||
#define bits_SLOT_AGRUNT_HORNET2 ( 1 << 5 )
|
||||
#define bits_SLOT_AGRUNT_CHASE ( 1 << 6 )
|
||||
#define bits_SLOTS_AGRUNT_HORNET ( bits_SLOT_AGRUNT_HORNET1 | bits_SLOT_AGRUNT_HORNET2 )
|
||||
|
||||
// HOUNDEYE SLOTS
|
||||
#define bits_SLOT_HOUND_ATTACK1 ( 1 << 7 )
|
||||
#define bits_SLOT_HOUND_ATTACK2 ( 1 << 8 )
|
||||
#define bits_SLOT_HOUND_ATTACK3 ( 1 << 9 )
|
||||
#define bits_SLOTS_HOUND_ATTACK ( bits_SLOT_HOUND_ATTACK1 | bits_SLOT_HOUND_ATTACK2 | bits_SLOT_HOUND_ATTACK3 )
|
||||
|
||||
// global slots
|
||||
#define bits_SLOT_SQUAD_SPLIT ( 1 << 10 )// squad members don't all have the same enemy
|
||||
|
||||
#define NUM_SLOTS 11// update this every time you add/remove a slot.
|
||||
|
||||
#define MAX_SQUAD_MEMBERS 5
|
||||
|
||||
//=========================================================
|
||||
// CSquadMonster - for any monster that forms squads.
|
||||
//=========================================================
|
||||
class CSquadMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
// squad leader info
|
||||
EHANDLE m_hSquadLeader; // who is my leader
|
||||
EHANDLE m_hSquadMember[MAX_SQUAD_MEMBERS-1]; // valid only for leader
|
||||
int m_afSquadSlots;
|
||||
float m_flLastEnemySightTime; // last time anyone in the squad saw the enemy
|
||||
BOOL m_fEnemyEluded;
|
||||
|
||||
// squad member info
|
||||
int m_iMySlot;// this is the behaviour slot that the monster currently holds in the squad.
|
||||
|
||||
int CheckEnemy ( CBaseEntity *pEnemy );
|
||||
void StartMonster ( void );
|
||||
void VacateSlot( void );
|
||||
void ScheduleChange( void );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
BOOL OccupySlot( int iDesiredSlot );
|
||||
BOOL NoFriendlyFire( void );
|
||||
BOOL NoFriendlyFire( BOOL playerAlly );
|
||||
|
||||
// squad functions still left in base class
|
||||
CSquadMonster *MySquadLeader( )
|
||||
{
|
||||
CSquadMonster *pSquadLeader = (CSquadMonster *)((CBaseEntity *)m_hSquadLeader);
|
||||
if (pSquadLeader != NULL)
|
||||
return pSquadLeader;
|
||||
return this;
|
||||
}
|
||||
CSquadMonster *MySquadMember( int i )
|
||||
{
|
||||
if (i >= MAX_SQUAD_MEMBERS-1)
|
||||
return this;
|
||||
else
|
||||
return (CSquadMonster *)((CBaseEntity *)m_hSquadMember[i]);
|
||||
}
|
||||
int InSquad ( void ) { return m_hSquadLeader != NULL; }
|
||||
int IsLeader ( void ) { return m_hSquadLeader == this; }
|
||||
int SquadJoin ( int searchRadius );
|
||||
int SquadRecruit ( int searchRadius, int maxMembers );
|
||||
int SquadCount( void );
|
||||
void SquadRemove( CSquadMonster *pRemove );
|
||||
void SquadUnlink( void );
|
||||
BOOL SquadAdd( CSquadMonster *pAdd );
|
||||
void SquadDisband( void );
|
||||
void SquadAddConditions ( int iConditions );
|
||||
void SquadMakeEnemy ( CBaseEntity *pEnemy );
|
||||
void SquadPasteEnemyInfo ( void );
|
||||
void SquadCopyEnemyInfo ( void );
|
||||
BOOL SquadEnemySplit ( void );
|
||||
BOOL SquadMemberInRange( const Vector &vecLocation, float flDist );
|
||||
|
||||
virtual CSquadMonster *MySquadMonsterPointer( void ) { return this; }
|
||||
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
int Save( CSave &save );
|
||||
int Restore( CRestore &restore );
|
||||
|
||||
BOOL FValidateCover ( const Vector &vecCoverLocation );
|
||||
|
||||
MONSTERSTATE GetIdealState ( void );
|
||||
Schedule_t *GetScheduleOfType ( int iType );
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,186 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
#ifndef TALKMONSTER_H
|
||||
#define TALKMONSTER_H
|
||||
|
||||
#ifndef MONSTERS_H
|
||||
#include "monsters.h"
|
||||
#endif
|
||||
|
||||
//=========================================================
|
||||
// Talking monster base class
|
||||
// Used for scientists and barneys
|
||||
//=========================================================
|
||||
|
||||
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
|
||||
|
||||
#define TLK_STARE_DIST 128 // anyone closer than this and looking at me is probably staring at me.
|
||||
|
||||
#define bit_saidDamageLight (1<<0) // bits so we don't repeat key sentences
|
||||
#define bit_saidDamageMedium (1<<1)
|
||||
#define bit_saidDamageHeavy (1<<2)
|
||||
#define bit_saidHelloPlayer (1<<3)
|
||||
#define bit_saidWoundLight (1<<4)
|
||||
#define bit_saidWoundHeavy (1<<5)
|
||||
#define bit_saidHeard (1<<6)
|
||||
#define bit_saidSmelled (1<<7)
|
||||
|
||||
#define TLK_CFRIENDS 3
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TLK_ANSWER = 0,
|
||||
TLK_QUESTION,
|
||||
TLK_IDLE,
|
||||
TLK_STARE,
|
||||
TLK_USE,
|
||||
TLK_UNUSE,
|
||||
TLK_DECLINE, //LRC- refuse to accompany
|
||||
TLK_STOP,
|
||||
TLK_NOSHOOT,
|
||||
TLK_HELLO,
|
||||
TLK_PHELLO,
|
||||
TLK_PIDLE,
|
||||
TLK_PQUESTION,
|
||||
TLK_PLHURT1,
|
||||
TLK_PLHURT2,
|
||||
TLK_PLHURT3,
|
||||
TLK_SMELL,
|
||||
TLK_WOUND,
|
||||
TLK_MORTAL,
|
||||
|
||||
TLK_CGROUPS, // MUST be last entry
|
||||
} TALKGROUPNAMES;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SCHED_CANT_FOLLOW = LAST_COMMON_SCHEDULE + 1,
|
||||
SCHED_MOVE_AWAY, // Try to get out of the player's way
|
||||
SCHED_MOVE_AWAY_FOLLOW, // same, but follow afterward
|
||||
SCHED_MOVE_AWAY_FAIL, // Turn back toward player
|
||||
|
||||
LAST_TALKMONSTER_SCHEDULE, // MUST be last
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TASK_CANT_FOLLOW = LAST_COMMON_TASK + 1,
|
||||
TASK_MOVE_AWAY_PATH,
|
||||
TASK_WALK_PATH_FOR_UNITS,
|
||||
|
||||
TASK_TLK_RESPOND, // say my response
|
||||
TASK_TLK_SPEAK, // question or remark
|
||||
TASK_TLK_HELLO, // Try to say hello to player
|
||||
TASK_TLK_HEADRESET, // reset head position
|
||||
TASK_TLK_STOPSHOOTING, // tell player to stop shooting friend
|
||||
TASK_TLK_STARE, // let the player know I know he's staring at me.
|
||||
TASK_TLK_LOOK_AT_CLIENT,// faces player if not moving and not talking and in idle.
|
||||
TASK_TLK_CLIENT_STARE, // same as look at client, but says something if the player stares.
|
||||
TASK_TLK_EYECONTACT, // maintain eyecontact with person who I'm talking to
|
||||
TASK_TLK_IDEALYAW, // set ideal yaw to face who I'm talking to
|
||||
TASK_FACE_PLAYER, // Face the player
|
||||
|
||||
LAST_TALKMONSTER_TASK, // MUST be last
|
||||
};
|
||||
|
||||
class CTalkMonster : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void TalkInit( void );
|
||||
CBaseEntity *FindNearestFriend(BOOL fPlayer);
|
||||
float TargetDistance( void );
|
||||
void StopTalking( void ) { SentenceStop(); }
|
||||
|
||||
// Base Monster functions
|
||||
void Precache( void );
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType);
|
||||
void Touch( CBaseEntity *pOther );
|
||||
void Killed( entvars_t *pevAttacker, int iGib );
|
||||
int IRelationship ( CBaseEntity *pTarget );
|
||||
virtual int CanPlaySentence( BOOL fDisregardState );
|
||||
virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation );
|
||||
void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
|
||||
void KeyValue( KeyValueData *pkvd );
|
||||
|
||||
// AI functions
|
||||
void SetActivity ( Activity newActivity );
|
||||
Schedule_t *GetScheduleOfType ( int Type );
|
||||
void StartTask( Task_t *pTask );
|
||||
void RunTask( Task_t *pTask );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
void PrescheduleThink( void );
|
||||
|
||||
|
||||
// Conversations / communication
|
||||
int GetVoicePitch( void );
|
||||
void IdleRespond( void );
|
||||
int FIdleSpeak( void );
|
||||
int FIdleStare( void );
|
||||
int FIdleHello( void );
|
||||
void IdleHeadTurn( Vector &vecFriend );
|
||||
int FOkToSpeak( void );
|
||||
void TrySmellTalk( void );
|
||||
CBaseEntity *EnumFriends( CBaseEntity *pentPrevious, int listNumber, BOOL bTrace );
|
||||
void AlertFriends( void );
|
||||
void ShutUpFriends( void );
|
||||
BOOL IsTalking( void );
|
||||
void Talk( float flDuration );
|
||||
// For following
|
||||
BOOL CanFollow( void );
|
||||
BOOL IsFollowing( void ) { return m_hTargetEnt != NULL && m_hTargetEnt->IsPlayer(); }
|
||||
void StopFollowing( BOOL clearSchedule );
|
||||
void StartFollowing( CBaseEntity *pLeader );
|
||||
virtual void DeclineFollowing( void ) {}
|
||||
void LimitFollowers( CBaseEntity *pPlayer, int maxFollowers );
|
||||
|
||||
void EXPORT FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
|
||||
|
||||
virtual void SetAnswerQuestion( CTalkMonster *pSpeaker );
|
||||
virtual int FriendNumber( int arrayNumber ) { return arrayNumber; }
|
||||
|
||||
virtual int Save( CSave &save );
|
||||
virtual int Restore( CRestore &restore );
|
||||
static TYPEDESCRIPTION m_SaveData[];
|
||||
|
||||
|
||||
static char *m_szFriends[TLK_CFRIENDS]; // array of friend names
|
||||
static float g_talkWaitTime;
|
||||
|
||||
int m_bitsSaid; // set bits for sentences we don't want repeated
|
||||
int m_nSpeak; // number of times initiated talking
|
||||
int m_voicePitch; // pitch of voice for this head
|
||||
const char *m_szGrp[TLK_CGROUPS]; // sentence group names
|
||||
float m_useTime; // Don't allow +USE until this time
|
||||
int m_iszUse; // Custom +USE sentence group (follow)
|
||||
int m_iszUnUse; // Custom +USE sentence group (stop following)
|
||||
int m_iszDecline; // Custom +USE sentence group (refuse to follow) LRC
|
||||
int m_iszSpeakAs; // Change the prefix for all this monster's speeches LRC
|
||||
|
||||
float m_flLastSaidSmelled;// last time we talked about something that stinks
|
||||
float m_flStopTalkTime;// when in the future that I'll be done saying this sentence.
|
||||
|
||||
EHANDLE m_hTalkTarget; // who to look at while talking
|
||||
CUSTOM_SCHEDULES;
|
||||
};
|
||||
|
||||
|
||||
// Clients can push talkmonsters out of their way
|
||||
#define bits_COND_CLIENT_PUSH ( bits_COND_SPECIAL1 )
|
||||
// Don't see a client right now.
|
||||
#define bits_COND_CLIENT_UNSEEN ( bits_COND_SPECIAL2 )
|
||||
|
||||
|
||||
#endif //TALKMONSTER_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,381 @@
|
|||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
* This product contains software technology licensed from Id
|
||||
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This source code contains proprietary and confidential information of
|
||||
* Valve LLC and its suppliers. Access to this code is restricted to
|
||||
* persons who have executed a written SDK license with Valve. Any access,
|
||||
* use or distribution of this code by or to any unlicensed person is illegal.
|
||||
*
|
||||
****/
|
||||
//=========================================================
|
||||
// Zombie
|
||||
//=========================================================
|
||||
|
||||
// UNDONE: Don't flinch every time you get hit
|
||||
|
||||
#include "extdll.h"
|
||||
#include "utils.h"
|
||||
#include "cbase.h"
|
||||
#include "monsters.h"
|
||||
#include "schedule.h"
|
||||
#include "defaults.h"
|
||||
|
||||
|
||||
//=========================================================
|
||||
// Monster's Anim Events Go Here
|
||||
//=========================================================
|
||||
#define ZOMBIE_AE_ATTACK_RIGHT 0x01
|
||||
#define ZOMBIE_AE_ATTACK_LEFT 0x02
|
||||
#define ZOMBIE_AE_ATTACK_BOTH 0x03
|
||||
#define ZOMBIE_AE_CRAB1 0x04
|
||||
#define ZOMBIE_AE_CRAB2 0x05
|
||||
#define ZOMBIE_AE_CRAB3 0x06
|
||||
|
||||
#define ZOMBIE_FLINCH_DELAY 2 // at most one flinch every n secs
|
||||
#define ZOMBIE_CRAB "monster_headcrab" // headcrab jumps from zombie
|
||||
|
||||
|
||||
class CZombie : public CBaseMonster
|
||||
{
|
||||
public:
|
||||
void Spawn( void );
|
||||
void Precache( void );
|
||||
void SetYawSpeed( void );
|
||||
int Classify ( void );
|
||||
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
||||
int IgnoreConditions ( void );
|
||||
void SpawnCrab( void );
|
||||
|
||||
float m_flNextFlinch;
|
||||
|
||||
void PainSound( void );
|
||||
void AlertSound( void );
|
||||
void IdleSound( void );
|
||||
void AttackSound( void );
|
||||
|
||||
static const char *pAttackSounds[];
|
||||
static const char *pIdleSounds[];
|
||||
static const char *pAlertSounds[];
|
||||
static const char *pPainSounds[];
|
||||
static const char *pAttackHitSounds[];
|
||||
static const char *pAttackMissSounds[];
|
||||
|
||||
// No range attacks
|
||||
BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; }
|
||||
BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; }
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
|
||||
};
|
||||
|
||||
LINK_ENTITY_TO_CLASS( monster_zombie, CZombie );
|
||||
|
||||
const char *CZombie::pAttackHitSounds[] =
|
||||
{
|
||||
"zombie/claw_strike1.wav",
|
||||
"zombie/claw_strike2.wav",
|
||||
"zombie/claw_strike3.wav",
|
||||
};
|
||||
|
||||
const char *CZombie::pAttackMissSounds[] =
|
||||
{
|
||||
"zombie/claw_miss1.wav",
|
||||
"zombie/claw_miss2.wav",
|
||||
};
|
||||
|
||||
const char *CZombie::pAttackSounds[] =
|
||||
{
|
||||
"zombie/zo_attack1.wav",
|
||||
"zombie/zo_attack2.wav",
|
||||
};
|
||||
|
||||
const char *CZombie::pIdleSounds[] =
|
||||
{
|
||||
"zombie/zo_idle1.wav",
|
||||
"zombie/zo_idle2.wav",
|
||||
"zombie/zo_idle3.wav",
|
||||
"zombie/zo_idle4.wav",
|
||||
};
|
||||
|
||||
const char *CZombie::pAlertSounds[] =
|
||||
{
|
||||
"zombie/zo_alert10.wav",
|
||||
"zombie/zo_alert20.wav",
|
||||
"zombie/zo_alert30.wav",
|
||||
};
|
||||
|
||||
const char *CZombie::pPainSounds[] =
|
||||
{
|
||||
"zombie/zo_pain1.wav",
|
||||
"zombie/zo_pain2.wav",
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
// Classify - indicates this monster's place in the
|
||||
// relationship table.
|
||||
//=========================================================
|
||||
int CZombie :: Classify ( void )
|
||||
{
|
||||
return m_iClass?m_iClass:CLASS_ALIEN_MONSTER;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// SetYawSpeed - allows each sequence to have a different
|
||||
// turn rate associated with it.
|
||||
//=========================================================
|
||||
void CZombie :: SetYawSpeed ( void )
|
||||
{
|
||||
pev->yaw_speed = 120;
|
||||
}
|
||||
|
||||
int CZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
|
||||
{
|
||||
// Take 30% damage from bullets
|
||||
if ( bitsDamageType == DMG_BULLET )
|
||||
{
|
||||
Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5;
|
||||
vecDir = vecDir.Normalize();
|
||||
float flForce = DamageForce( flDamage );
|
||||
pev->velocity = pev->velocity + vecDir * flForce;
|
||||
flDamage *= 0.3;
|
||||
}
|
||||
|
||||
// HACK HACK -- until we fix this.
|
||||
if ( IsAlive() )
|
||||
PainSound();
|
||||
return CBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
|
||||
}
|
||||
|
||||
void CZombie :: PainSound( void )
|
||||
{
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
if (RANDOM_LONG(0,5) < 2)
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
void CZombie :: AlertSound( void )
|
||||
{
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch );
|
||||
}
|
||||
|
||||
void CZombie :: IdleSound( void )
|
||||
{
|
||||
int pitch = 95 + RANDOM_LONG(0,9);
|
||||
|
||||
// Play a random idle sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
void CZombie :: AttackSound( void )
|
||||
{
|
||||
// Play a random attack sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
|
||||
|
||||
//=========================================================
|
||||
// HandleAnimEvent - catches the monster-specific messages
|
||||
// that occur when tagged animation frames are played.
|
||||
//=========================================================
|
||||
void CZombie :: HandleAnimEvent( MonsterEvent_t *pEvent )
|
||||
{
|
||||
switch( pEvent->event )
|
||||
{
|
||||
case ZOMBIE_AE_ATTACK_RIGHT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, ZOMBIE_ONE_SLASH, DMG_SLASH );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) )
|
||||
{
|
||||
pHurt->pev->punchangle.z = -18;
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
pHurt->pev->velocity = pHurt->pev->velocity - gpGlobals->v_right * 100;
|
||||
}
|
||||
// Play a random attack hit sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
else // Play a random attack miss sound
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
|
||||
if (RANDOM_LONG(0,1))
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_ATTACK_LEFT:
|
||||
{
|
||||
// do stuff for this event.
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, ZOMBIE_ONE_SLASH, DMG_SLASH );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) )
|
||||
{
|
||||
pHurt->pev->punchangle.z = 18;
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_right * 100;
|
||||
}
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
else
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
|
||||
if (RANDOM_LONG(0,1))
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_ATTACK_BOTH:
|
||||
{
|
||||
// do stuff for this event.
|
||||
CBaseEntity *pHurt = CheckTraceHullAttack( 70, ZOMBIE_BOTH_SLASH, DMG_SLASH );
|
||||
if ( pHurt )
|
||||
{
|
||||
if ( pHurt->pev->flags & (FL_MONSTER|FL_CLIENT) )
|
||||
{
|
||||
pHurt->pev->punchangle.x = 5;
|
||||
pHurt->pev->velocity = pHurt->pev->velocity + gpGlobals->v_forward * -100;
|
||||
}
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
}
|
||||
else
|
||||
EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) );
|
||||
|
||||
if (RANDOM_LONG(0,1))
|
||||
AttackSound();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_CRAB1:
|
||||
{
|
||||
pev->body = 1; // set the head to a skull
|
||||
SpawnCrab();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_CRAB2:
|
||||
{
|
||||
pev->body = 1; // set the head to a skull
|
||||
SpawnCrab();
|
||||
}
|
||||
break;
|
||||
|
||||
case ZOMBIE_AE_CRAB3:
|
||||
{
|
||||
pev->body = 1; // set the head to a skull
|
||||
SpawnCrab();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CBaseMonster::HandleAnimEvent( pEvent );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn
|
||||
//=========================================================
|
||||
void CZombie :: Spawn()
|
||||
{
|
||||
Precache();
|
||||
|
||||
if (pev->model) UTIL_SetModel(ENT(pev), pev->model);
|
||||
else
|
||||
{
|
||||
if(pev->button == 0) UTIL_SetModel(ENT(pev), "models/monsters/zombie_sci.mdl");
|
||||
else if(pev->button == 1) UTIL_SetModel(ENT(pev), "models/monsters/zombie_bar.mdl");
|
||||
}
|
||||
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
|
||||
|
||||
pev->solid = SOLID_SLIDEBOX;
|
||||
pev->movetype = MOVETYPE_STEP;
|
||||
m_bloodColor = BLOOD_COLOR_GREEN;
|
||||
if (pev->health == 0)
|
||||
pev->health = ZOMBIE_HEALTH;
|
||||
pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
|
||||
m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
|
||||
m_MonsterState = MONSTERSTATE_NONE;
|
||||
m_afCapability = bits_CAP_DOORS_GROUP;
|
||||
|
||||
MonsterInit();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Precache - precaches all resources this monster needs
|
||||
//=========================================================
|
||||
void CZombie :: Precache()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (pev->model) UTIL_PrecacheModel(pev->model); //LRC
|
||||
else
|
||||
{
|
||||
if(pev->button) UTIL_PrecacheModel("models/monsters/zombie_bar.mdl");
|
||||
else UTIL_PrecacheModel("models/monsters/zombie_sci.mdl");
|
||||
}
|
||||
UTIL_PrecacheEntity( ZOMBIE_CRAB );
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackHitSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackMissSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAttackSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pIdleSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pAlertSounds[i]);
|
||||
|
||||
for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ )
|
||||
PRECACHE_SOUND((char *)pPainSounds[i]);
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// AI Schedules Specific to this monster
|
||||
//=========================================================
|
||||
|
||||
|
||||
|
||||
int CZombie::IgnoreConditions ( void )
|
||||
{
|
||||
int iIgnore = CBaseMonster::IgnoreConditions();
|
||||
|
||||
if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK1))
|
||||
{
|
||||
if (m_flNextFlinch >= gpGlobals->time)
|
||||
iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE);
|
||||
}
|
||||
|
||||
if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH))
|
||||
{
|
||||
if (m_flNextFlinch < gpGlobals->time)
|
||||
m_flNextFlinch = gpGlobals->time + ZOMBIE_FLINCH_DELAY;
|
||||
}
|
||||
|
||||
return iIgnore;
|
||||
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
// Spawn Headcrab - headcrab jumps from zombie
|
||||
//=========================================================
|
||||
void CZombie :: SpawnCrab( void )
|
||||
{
|
||||
vec3_t vecSrc, vecAng;
|
||||
GetAttachment( 0, vecSrc, vecAng );
|
||||
CBaseEntity *pCrab = CBaseEntity::Create( ZOMBIE_CRAB, vecSrc, vecAng, edict() ); // create the crab
|
||||
pCrab->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND; // make the crab fall
|
||||
pCrab->pev->velocity = pev->movedir * 30;
|
||||
pCrab->pev->velocity.z += 30;
|
||||
}
|
8
todo.log
8
todo.log
|
@ -113,11 +113,13 @@ Beta 13.12.08
|
|||
90. get rid of Com_ParseExt OK
|
||||
91. implement rendermodes OK
|
||||
92. implement VBO OK
|
||||
93. implement sky rotate, sky shader, etc
|
||||
93. implement sky rotate, sky shader, etc OK
|
||||
94. implement studio format
|
||||
95. support for custom tables (external) OK
|
||||
96. implement sprite format OK
|
||||
97. fix fog color in release build OK
|
||||
97. fix fog in release build
|
||||
98. fix crash in release build
|
||||
99. rewrote RF_* flags OK
|
||||
100. implement JpegLib ?
|
||||
100. implement JpegLib OK
|
||||
101. replace Matrix_ with Matrix3x3_ OK
|
||||
102. replace Matrix4_ with Matrix4x4_ ?
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,712 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2007 ©
|
||||
// pr_local.h - virtual machine definitions
|
||||
//=======================================================================
|
||||
#ifndef PR_LOCAL_H
|
||||
#define PR_LOCAL_H
|
||||
|
||||
#include <setjmp.h>
|
||||
#include "byteorder.h"
|
||||
|
||||
/*
|
||||
|
||||
TODO:
|
||||
other pointer types for models and clients?
|
||||
compact string heap?
|
||||
always initialize all variables to something safe
|
||||
the def->type->type arrangement is really silly.
|
||||
return type checking
|
||||
parm count type checking
|
||||
immediate overflow checking
|
||||
pass the first two parms in call->b and call->c
|
||||
*/
|
||||
|
||||
#define MEMBERFIELDNAME "__m%s" // mark
|
||||
#define MAX_NAME 64 // chars long
|
||||
#define MAX_PARMS_EXTRA 128
|
||||
#define PROGDEFS_MAX_SIZE MAX_MSGLEN // 32 kbytes
|
||||
|
||||
// optimization level flags
|
||||
#define FL_DBG 1
|
||||
#define FL_OP0 2
|
||||
#define FL_OP1 4
|
||||
#define FL_OP2 8
|
||||
#define FL_OP3 16
|
||||
#define FL_OP4 32
|
||||
|
||||
#define MASK_DEBUG (FL_DBG)
|
||||
#define MASK_LEVEL_O (FL_OP0)
|
||||
#define MASK_LEVEL_1 (FL_OP0|FL_OP1)
|
||||
#define MASK_LEVEL_2 (FL_OP0|FL_OP1|FL_OP2)
|
||||
#define MASK_LEVEL_3 (FL_OP0|FL_OP1|FL_OP2|FL_OP3)
|
||||
#define MASK_LEVEL_4 (FL_OP0|FL_OP1|FL_OP2|FL_OP3|FL_OP4)
|
||||
|
||||
//compiler flags
|
||||
#define FLAG_V7_ONLY 1 // only 7 version can use this optimization
|
||||
#define FLAG_DEFAULT 2 // enabled as default
|
||||
|
||||
#define G_FLOAT(o) (pr_globals[o])
|
||||
#define G_INT(o) (*(int *)&pr_globals[o])
|
||||
#define G_VECTOR(o) (&pr_globals[o])
|
||||
#define G_STRING(o) (strings + *(string_t *)&pr_globals[o])
|
||||
#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
|
||||
|
||||
//qcc private pool
|
||||
#define Qalloc( size ) Mem_Alloc(qccpool, size )
|
||||
#define Qrealloc( ptr, size ) Mem_Realloc(qccpool, ptr, size )
|
||||
|
||||
extern int com_argc;
|
||||
extern char **com_argv;
|
||||
|
||||
//loading files
|
||||
#define QCC_LoadFile(f, c) PR_LoadFile(f, c, FT_CODE)
|
||||
#define QCC_LoadData(f) PR_LoadFile(f, true, FT_DATA)
|
||||
|
||||
#define BytesForBuckets(b) (sizeof(bucket_t)*b)
|
||||
#define STRCMP(s1, s2) (((*s1)!=(*s2)) || com.strcmp(s1+1,s2+1))
|
||||
#define STRNCMP(s1, s2, l) (((*s1)!=(*s2)) || com.strncmp(s1+1,s2+1,l))
|
||||
|
||||
typedef uint gofs_t; // offset in global data block
|
||||
typedef struct function_s function_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
comp_inactive = 0,
|
||||
comp_begin,
|
||||
comp_frame,
|
||||
comp_done,
|
||||
comp_error,
|
||||
} comp_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
tt_eof, // end of file reached
|
||||
tt_name, // an alphanumeric name token
|
||||
tt_punct, // code punctuation
|
||||
tt_immediate, // string, float, vector
|
||||
} token_type_t;
|
||||
|
||||
enum
|
||||
{
|
||||
// progs 6 keywords
|
||||
KEYWORD_DO,
|
||||
KEYWORD_IF,
|
||||
KEYWORD_VOID,
|
||||
KEYWORD_ELSE,
|
||||
KEYWORD_LOCAL, // legacy ( not used )
|
||||
KEYWORD_WHILE,
|
||||
KEYWORD_ENTITY,
|
||||
KEYWORD_FLOAT,
|
||||
KEYWORD_STRING,
|
||||
KEYWORD_VECTOR,
|
||||
KEYWORD_RETURN,
|
||||
|
||||
// 6 extended (no new opcode used)
|
||||
KEYWORD_BREAK,
|
||||
KEYWORD_FOR,
|
||||
KEYWORD_CONTINUE,
|
||||
KEYWORD_CONST,
|
||||
|
||||
// progs 7 keywords
|
||||
KEYWORD_ENUM,
|
||||
KEYWORD_ENUMFLAGS,
|
||||
KEYWORD_CASE,
|
||||
KEYWORD_DEFAULT,
|
||||
KEYWORD_GOTO,
|
||||
KEYWORD_INT,
|
||||
KEYWORD_STATE,
|
||||
KEYWORD_CLASS,
|
||||
KEYWORD_STRUCT,
|
||||
KEYWORD_SWITCH,
|
||||
KEYWORD_TYPEDEF,
|
||||
KEYWORD_EXTERN,
|
||||
KEYWORD_UNION,
|
||||
KEYWORD_THINKTIME,
|
||||
|
||||
// progs 8 keywords
|
||||
KEYWORD_BOOL,
|
||||
KEYWORD_ASM,
|
||||
|
||||
KEYWORD_SHARED,
|
||||
KEYWORD_NOSAVE,
|
||||
NUM_KEYWORDS
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ev_void,
|
||||
ev_string,
|
||||
ev_float,
|
||||
ev_vector,
|
||||
ev_entity,
|
||||
ev_field,
|
||||
ev_function,
|
||||
ev_pointer,
|
||||
ev_integer,
|
||||
ev_struct,
|
||||
ev_union,
|
||||
ev_bool,
|
||||
} etype_t;
|
||||
|
||||
typedef struct bucket_s
|
||||
{
|
||||
void *data;
|
||||
char *keystring;
|
||||
struct bucket_s *next;
|
||||
} bucket_t;
|
||||
|
||||
typedef struct hashtable_s
|
||||
{
|
||||
uint numbuckets;
|
||||
bucket_t **bucket;
|
||||
} hashtable_t;
|
||||
|
||||
typedef struct cachedsourcefile_s
|
||||
{
|
||||
char filename[128];
|
||||
int size;
|
||||
byte *file;
|
||||
enum
|
||||
{
|
||||
FT_CODE, // quakec source
|
||||
FT_DATA, // quakec lib
|
||||
} type;
|
||||
struct cachedsourcefile_s *next; // chain
|
||||
} cachedsourcefile_t;
|
||||
|
||||
struct function_s
|
||||
{
|
||||
int builtin; // if non 0, call an internal function
|
||||
int code; // first statement
|
||||
char *file; // source file with definition
|
||||
int file_line;
|
||||
struct def_s *def;
|
||||
uint parm_ofs[MAX_PARMS];// always contiguous, right?
|
||||
};
|
||||
|
||||
typedef struct type_s
|
||||
{
|
||||
etype_t type;
|
||||
|
||||
struct type_s *parentclass; // type_entity...
|
||||
struct type_s *next;
|
||||
struct type_s *aux_type; // return type or field type
|
||||
struct type_s *param;
|
||||
|
||||
int num_parms; // -1 = variable args
|
||||
uint ofs; // inside a structure.
|
||||
uint size;
|
||||
char *name;
|
||||
} type_t;
|
||||
|
||||
typedef struct temp_s
|
||||
{
|
||||
gofs_t ofs;
|
||||
struct def_s *scope;
|
||||
struct def_s *lastfunc;
|
||||
struct temp_s *next;
|
||||
bool used;
|
||||
uint size;
|
||||
} temp_t;
|
||||
|
||||
typedef union eval_s
|
||||
{
|
||||
float _float;
|
||||
int _int;
|
||||
float vector[3];
|
||||
func_t function;
|
||||
string_t string;
|
||||
union eval_s *ptr;
|
||||
} eval_t;
|
||||
|
||||
// virtual typedef
|
||||
typedef union prvm_eval_s
|
||||
{
|
||||
int edict;
|
||||
int _int;
|
||||
float _float;
|
||||
float vector[3];
|
||||
func_t function;
|
||||
string_t string;
|
||||
} prvm_eval_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool *enabled;
|
||||
char *shortname; // for option "/Ox", where x is shortname
|
||||
char *fullname; // display name
|
||||
int levelmask; // optimization level mask
|
||||
int flags; // sepcial flags for kill debug extensions, e.t.c
|
||||
} optimisations_t;
|
||||
|
||||
typedef struct keyword_s
|
||||
{
|
||||
int number;
|
||||
const char *name; // first name
|
||||
const char *alias; // alias for keyword
|
||||
uint ignoretype; // ignore matching for this type
|
||||
} keyword_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[MAX_NAME];
|
||||
char value[MAX_NAME * 4];
|
||||
char params[MAX_PARMS][32];
|
||||
int numparams;
|
||||
bool used;
|
||||
bool inside;
|
||||
int namelen;
|
||||
} const_t;
|
||||
|
||||
typedef struct includechunk_s
|
||||
{
|
||||
struct includechunk_s *prev;
|
||||
char *filename;
|
||||
char *currentdatapoint;
|
||||
int currentlinenumber;
|
||||
const_t *cnst;
|
||||
} includechunk_t;
|
||||
|
||||
typedef struct freeoffset_s
|
||||
{
|
||||
struct freeoffset_s *next;
|
||||
gofs_t ofs;
|
||||
uint size;
|
||||
|
||||
} freeoffset_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
char *opname;
|
||||
int priority;
|
||||
|
||||
enum
|
||||
{
|
||||
ASSOC_LEFT,
|
||||
ASSOC_RIGHT,
|
||||
} associative;
|
||||
|
||||
type_t **type_a;
|
||||
type_t **type_b;
|
||||
type_t **type_c;
|
||||
} opcode_t;
|
||||
|
||||
typedef struct def_s
|
||||
{
|
||||
type_t *type;
|
||||
char *name;
|
||||
struct def_s *next;
|
||||
struct def_s *nextlocal; // provides a chain of local variables
|
||||
gofs_t ofs; // for the opt_locals_marshalling optimisation.
|
||||
struct def_s *scope; // function the var was defined in, or NULL
|
||||
int initialized; // 1 when a declaration included "= immediate"
|
||||
int constant; // 1 says we can use the value over and over again
|
||||
bool local; // 1 indices local variable
|
||||
|
||||
int references;
|
||||
int timescalled; // part of the opt_stripfunctions optimisation.
|
||||
|
||||
int s_file;
|
||||
int s_line;
|
||||
|
||||
int arraysize;
|
||||
bool shared;
|
||||
bool saved;
|
||||
|
||||
temp_t *temp;
|
||||
} def_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
type_t *types;
|
||||
|
||||
def_t def_head; // unused head of linked list
|
||||
def_t *def_tail; // add new defs after this and move it
|
||||
def_t *localvars; // chain of variables which need to be pushed and stuff.
|
||||
|
||||
int size_fields;
|
||||
} pr_info_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
WARN_DEBUGGING,
|
||||
WARN_ERROR,
|
||||
WARN_NOTREFERENCED,
|
||||
WARN_NOTREFERENCEDCONST,
|
||||
WARN_CONFLICTINGRETURNS,
|
||||
WARN_TOOFEWPARAMS,
|
||||
WARN_TOOMANYPARAMS,
|
||||
WARN_UNEXPECTEDPUNCT,
|
||||
WARN_ASSIGNMENTTOCONSTANT,
|
||||
WARN_ASSIGNMENTTOCONSTANTFUNC,
|
||||
WARN_MISSINGRETURNVALUE,
|
||||
WARN_WRONGRETURNTYPE,
|
||||
WARN_POINTLESSSTATEMENT,
|
||||
WARN_MISSINGRETURN,
|
||||
WARN_DUPLICATEDEFINITION,
|
||||
WARN_UNDEFNOTDEFINED,
|
||||
WARN_PRECOMPILERMESSAGE,
|
||||
WARN_TOOMANYPARAMETERSFORFUNC,
|
||||
WARN_STRINGTOOLONG,
|
||||
WARN_BADTARGET,
|
||||
WARN_BADPRAGMA,
|
||||
WARN_HANGINGSLASHR,
|
||||
WARN_NOTDEFINED,
|
||||
WARN_NOTCONSTANT,
|
||||
WARN_SWITCHTYPEMISMATCH,
|
||||
WARN_CONFLICTINGUNIONMEMBER,
|
||||
WARN_ENUMFLAGS_NOTINTEGER,
|
||||
WARN_ENUMFLAGS_NOTBINARY,
|
||||
WARN_CASEINSENSATIVEFRAMEMACRO,
|
||||
WARN_DUPLICATELABEL,
|
||||
WARN_DUPLICATEMACRO,
|
||||
WARN_ASSIGNMENTINCONDITIONAL,
|
||||
WARN_MACROINSTRING,
|
||||
WARN_BADPARAMS,
|
||||
WARN_IMPLICITCONVERSION,
|
||||
WARN_FIXEDRETURNVALUECONFLICT,
|
||||
WARN_EXTRAPRECACHE,
|
||||
WARN_NOTPRECACHED,
|
||||
WARN_DEADCODE,
|
||||
WARN_UNREACHABLECODE,
|
||||
WARN_NOTSTANDARDBEHAVIOUR,
|
||||
WARN_INEFFICIENTPLUSPLUS,
|
||||
WARN_DUPLICATEPRECOMPILER,
|
||||
WARN_IDENTICALPRECOMPILER,
|
||||
WARN_EXTENSION_USED, //extension that frikqcc also understands
|
||||
WARN_IFSTRING_USED,
|
||||
WARN_LAXCAST, //some errors become this with a compiler flag
|
||||
WARN_UNDESIRABLECONVENTION,
|
||||
WARN_SAMENAMEASGLOBAL,
|
||||
WARN_CONSTANTCOMPARISON,
|
||||
WARN_IMAGETOOBIG,
|
||||
WARN_IGNOREDONLEFT,
|
||||
|
||||
// decompiling warns
|
||||
WARN_UNKNOWNTEMPTYPE, //
|
||||
|
||||
ERR_PARSEERRORS, //caused by pr_parseerror being called.
|
||||
|
||||
// these are definatly my fault...
|
||||
ERR_INTERNAL,
|
||||
ERR_TOOCOMPLEX,
|
||||
ERR_BADOPCODE,
|
||||
ERR_TOOMANYSTATEMENTS,
|
||||
ERR_TOOMANYSTRINGS,
|
||||
ERR_BADTARGETSWITCH,
|
||||
ERR_TOOMANYTYPES,
|
||||
ERR_TOOMANYPAKFILES,
|
||||
ERR_PRECOMPILERCONSTANTTOOLONG,
|
||||
ERR_MACROTOOMANYPARMS,
|
||||
ERR_CONSTANTTOOLONG,
|
||||
ERR_TOOMANYFRAMEMACROS,
|
||||
|
||||
// limitations, some are imposed by compiler, some arn't.
|
||||
ERR_TOOMANYGLOBALS,
|
||||
ERR_TOOMANYGOTOS,
|
||||
ERR_TOOMANYBREAKS,
|
||||
ERR_TOOMANYCONTINUES,
|
||||
ERR_TOOMANYCASES,
|
||||
ERR_TOOMANYLABELS,
|
||||
ERR_TOOMANYOPENFILES,
|
||||
ERR_TOOMANYPARAMETERSVARARGS,
|
||||
ERR_TOOMANYTOTALPARAMETERS,
|
||||
|
||||
// these are probably yours, or qcc being fussy.
|
||||
ERR_BADEXTENSION,
|
||||
ERR_BADIMMEDIATETYPE,
|
||||
ERR_NOOUTPUT,
|
||||
ERR_NOTAFUNCTION,
|
||||
ERR_FUNCTIONWITHVARGS,
|
||||
ERR_BADHEX,
|
||||
ERR_UNKNOWNPUCTUATION,
|
||||
ERR_EXPECTED,
|
||||
ERR_NOTANAME,
|
||||
ERR_NAMETOOLONG,
|
||||
ERR_NOFUNC,
|
||||
ERR_COULDNTOPENFILE,
|
||||
ERR_NOTFUNCTIONTYPE,
|
||||
ERR_TOOFEWPARAMS,
|
||||
ERR_TOOMANYPARAMS,
|
||||
ERR_CONSTANTNOTDEFINED,
|
||||
ERR_BADFRAMEMACRO,
|
||||
ERR_TYPEMISMATCH,
|
||||
ERR_TYPEMISMATCHREDEC,
|
||||
ERR_TYPEMISMATCHPARM,
|
||||
ERR_TYPEMISMATCHARRAYSIZE,
|
||||
ERR_UNEXPECTEDPUNCTUATION,
|
||||
ERR_NOTACONSTANT,
|
||||
ERR_REDECLARATION,
|
||||
ERR_INITIALISEDLOCALFUNCTION,
|
||||
ERR_NOTDEFINED,
|
||||
ERR_ARRAYNEEDSSIZE,
|
||||
ERR_ARRAYNEEDSBRACES,
|
||||
ERR_TOOMANYINITIALISERS,
|
||||
ERR_TYPEINVALIDINSTRUCT,
|
||||
ERR_NOSHAREDLOCALS,
|
||||
ERR_TYPEWITHNONAME,
|
||||
ERR_BADARRAYSIZE,
|
||||
ERR_NONAME,
|
||||
ERR_SHAREDINITIALISED,
|
||||
ERR_UNKNOWNVALUE,
|
||||
ERR_BADARRAYINDEXTYPE,
|
||||
ERR_NOVALIDOPCODES,
|
||||
ERR_MEMBERNOTVALID,
|
||||
ERR_BADPLUSPLUSOPERATOR,
|
||||
ERR_BADNOTTYPE,
|
||||
ERR_BADTYPECAST,
|
||||
ERR_MULTIPLEDEFAULTS,
|
||||
ERR_CASENOTIMMEDIATE,
|
||||
ERR_BADSWITCHTYPE,
|
||||
ERR_BADLABELNAME,
|
||||
ERR_NOLABEL,
|
||||
ERR_THINKTIMETYPEMISMATCH,
|
||||
ERR_STATETYPEMISMATCH,
|
||||
ERR_BADBUILTINIMMEDIATE,
|
||||
ERR_PARAMWITHNONAME,
|
||||
ERR_ILLEGALNAME,
|
||||
ERR_BADPARAMORDER,
|
||||
ERR_ILLEGALCONTINUES,
|
||||
ERR_ILLEGALBREAKS,
|
||||
ERR_ILLEGALCASES,
|
||||
ERR_NOTANUMBER,
|
||||
ERR_WRONGSUBTYPE,
|
||||
ERR_EOF,
|
||||
ERR_NOPRECOMPILERIF,
|
||||
ERR_NOENDIF,
|
||||
ERR_HASHERROR,
|
||||
ERR_NOTATYPE,
|
||||
ERR_TOOMANYPACKFILES,
|
||||
ERR_INVALIDVECTORIMMEDIATE,
|
||||
ERR_INVALIDSTRINGIMMEDIATE,
|
||||
ERR_BADCHARACTURECODE,
|
||||
ERR_BADPARMS,
|
||||
ERR_EXCEEDERRCOUNT,
|
||||
ERR_PRECOMPILERMESSAGE,
|
||||
|
||||
WARN_MAX,
|
||||
};
|
||||
|
||||
|
||||
extern pr_info_t pr;
|
||||
extern byte *qccpool;
|
||||
extern file_t *asmfile;
|
||||
extern int MAX_ERRORS;
|
||||
extern char *compilingfile;
|
||||
extern char progsoutname[MAX_SYSPATH];
|
||||
extern char sourcedir[MAX_SYSPATH];
|
||||
extern int recursivefunctiontype;
|
||||
extern freeoffset_t *freeofs;
|
||||
extern type_t *type_void;
|
||||
extern type_t *type_string;
|
||||
extern type_t *type_float;
|
||||
extern type_t *type_vector;
|
||||
extern type_t *type_entity;
|
||||
extern type_t *type_field;
|
||||
extern type_t *type_function;
|
||||
extern type_t *type_pointer;
|
||||
extern type_t *type_integer;
|
||||
extern type_t *type_floatfield;
|
||||
extern includechunk_t *currentchunk;
|
||||
extern cachedsourcefile_t *sourcefile;
|
||||
extern optimisations_t pr_optimisations[];
|
||||
extern char v_copyright[1024];
|
||||
extern hashtable_t compconstantstable;
|
||||
extern hashtable_t globalstable, localstable;
|
||||
extern hashtable_t intconstdefstable;
|
||||
extern hashtable_t floatconstdefstable;
|
||||
extern hashtable_t stringconstdefstable;
|
||||
extern opcode_t pr_opcodes[]; // sized by initialization
|
||||
extern keyword_t pr_keywords[];
|
||||
extern temp_t *functemps;
|
||||
const extern int type_size[];
|
||||
extern const_t *CompilerConstant;
|
||||
extern bool autoprototype;
|
||||
extern bool bodylessfuncs;
|
||||
extern char pr_token[8192];
|
||||
extern token_type_t pr_token_type;
|
||||
extern type_t *pr_immediate_type;
|
||||
extern eval_t pr_immediate;
|
||||
extern char *basictypenames[];
|
||||
extern int host_instance;
|
||||
extern int prvm_state;
|
||||
|
||||
extern bool opt_laxcasts;
|
||||
extern bool opt_ifstring;
|
||||
extern bool opt_overlaptemps;
|
||||
extern bool opt_shortenifnots;
|
||||
extern bool opt_noduplicatestrings;
|
||||
extern bool opt_constantarithmatic;
|
||||
extern bool opt_nonvec_parms;
|
||||
extern bool opt_constant_names;
|
||||
extern bool opt_precache_file;
|
||||
extern bool opt_filenames;
|
||||
extern bool opt_assignments;
|
||||
extern bool opt_unreferenced;
|
||||
extern bool opt_function_names;
|
||||
extern bool opt_locals;
|
||||
extern bool opt_dupconstdefs;
|
||||
extern bool opt_constant_names_strings;
|
||||
extern bool opt_return_only;
|
||||
extern bool opt_compound_jumps;
|
||||
extern bool opt_stripfunctions;
|
||||
extern bool opt_locals_marshalling;
|
||||
extern bool opt_logicops;
|
||||
extern bool opt_vectorcalls;
|
||||
extern bool opt_writelinenums;
|
||||
extern bool opt_writetypes;
|
||||
extern bool opt_writesources;
|
||||
extern bool opt_compstrings; // compress all strings into produced file
|
||||
extern bool opt_compfunctions; // compress all functions and statements
|
||||
extern bool opt_compress_other;
|
||||
extern bool pr_subscopedlocals;
|
||||
extern bool pr_warning[WARN_MAX];
|
||||
extern char pr_parm_names[MAX_PARMS + MAX_PARMS_EXTRA][MAX_NAME];
|
||||
extern def_t *extra_parms[MAX_PARMS_EXTRA];
|
||||
extern jmp_buf pr_parse_abort; // longjump with this on parse error
|
||||
extern jmp_buf pr_int_error; // casued in-game instead of Host_Error
|
||||
extern int pr_source_line;
|
||||
extern char *pr_file_p;
|
||||
extern def_t *pr_scope;
|
||||
extern int pr_bracelevel;
|
||||
extern int pr_error_count;
|
||||
extern int pr_total_error_count;
|
||||
extern int pr_warning_count;
|
||||
extern int ForcedCRC;
|
||||
extern string_t s_file, s_file2; // filename for function definition
|
||||
extern def_t def_ret;
|
||||
extern def_t def_parms[MAX_PARMS];
|
||||
extern char pr_immediate_string[8192];
|
||||
extern char sourcefilename[MAX_SYSPATH];
|
||||
extern float *pr_globals;
|
||||
extern uint numpr_globals;
|
||||
extern char *strings;
|
||||
extern int strofs;
|
||||
extern dstatement_t *statements;
|
||||
extern int numstatements;
|
||||
extern int *statement_linenums;
|
||||
extern dfunction_t *functions;
|
||||
extern int numfunctions;
|
||||
extern ddef_t *qcc_globals;
|
||||
extern int numglobaldefs;
|
||||
extern def_t *activetemps;
|
||||
extern ddef_t *fields;
|
||||
extern int numfielddefs;
|
||||
extern type_t *qcc_typeinfo;
|
||||
extern int numtypeinfos;
|
||||
extern int maxtypeinfos;
|
||||
extern int numtemps;
|
||||
|
||||
//
|
||||
// pr_main.c
|
||||
//
|
||||
void PR_InitCompile( const char *name );
|
||||
void PR_InitDecompile( const char *name );
|
||||
bool PRVM_DecompileProgs( const char *name );
|
||||
|
||||
//
|
||||
// pr_utils.c
|
||||
//
|
||||
extern int typecmp(type_t *a, type_t *b);
|
||||
void Hash_InitTable(hashtable_t *table, int numbucks);
|
||||
uint Hash_Key(char *name, int modulus);
|
||||
void *Hash_Get(hashtable_t *table, char *name);
|
||||
void *Hash_GetKey(hashtable_t *table, int key);
|
||||
void Hash_Remove(hashtable_t *table, char *name);
|
||||
void Hash_RemoveKey(hashtable_t *table, int key);
|
||||
void *Hash_GetNext(hashtable_t *table, char *name, void *old);
|
||||
void Hash_RemoveData(hashtable_t *table, char *name, void *data);
|
||||
void *Hash_Add(hashtable_t *table, char *name, void *data, bucket_t *buck);
|
||||
void *Hash_AddKey(hashtable_t *table, int key, void *data, bucket_t *buck);
|
||||
void PR_WriteBlock(file_t *f, fs_offset_t pos, const void *data, size_t blocksize, bool compress);
|
||||
bool PR_LoadingProject( const char *filename );
|
||||
void PR_SetDefaultProperties( void );
|
||||
word PR_WriteProgdefs( void );
|
||||
void PR_WriteLNOfile(char *filename);
|
||||
byte *PR_CreateProgsSRC( void );
|
||||
void PR_WriteDAT( void );
|
||||
|
||||
//
|
||||
// pr_lex.c
|
||||
//
|
||||
int PR_LexInteger (void);
|
||||
void PR_LexString (void);
|
||||
void PR_LexWhitespace (void);
|
||||
bool PR_SimpleGetToken (void);
|
||||
bool PR_Include(char *filename);
|
||||
bool PR_UndefineName(char *name);
|
||||
void PR_ConditionCompilation(void);
|
||||
char *PR_CheakCompConstString(char *def);
|
||||
const_t *PR_CheckCompConstDefined(char *def);
|
||||
type_t *PR_NewType (char *name, int basictype);
|
||||
char *PR_ParseToken( const char **data_p, bool allow_newline );
|
||||
|
||||
//
|
||||
// pr_comp.c
|
||||
//
|
||||
void PR_Lex( void );
|
||||
void PR_ParseAsm(void);
|
||||
bool PR_UnInclude( void );
|
||||
void PR_PrintDefs( void );
|
||||
void PR_ParseState (void);
|
||||
char *PR_ParseName( void );
|
||||
void PR_Expect (char *string);
|
||||
char *TypeName( type_t *type );
|
||||
void PR_SkipToSemicolon( void );
|
||||
type_t *TypeForName(char *name);
|
||||
void PR_ClearGrabMacros( void );
|
||||
def_t *PR_MakeIntDef(int value);
|
||||
void PR_UnmarshalLocals( void );
|
||||
void PR_NewLine (bool incomment);
|
||||
bool PR_CheckToken (char *string);
|
||||
bool PR_CheckName (char *string);
|
||||
const_t *PR_DefineName(char *name);
|
||||
type_t *PR_ParseType (int newtype);
|
||||
type_t *PR_FindType (type_t *type);
|
||||
bool PR_MatchKeyword( int keyword );
|
||||
def_t *PR_MakeFloatDef(float value);
|
||||
def_t *PR_MakeStringDef(char *value);
|
||||
bool PR_KeywordEnabled( int keyword );
|
||||
void PR_Message( char *message, ... );
|
||||
bool PR_CheckForKeyword( int keyword );
|
||||
type_t *PR_FieldType (type_t *pointsto);
|
||||
void PR_PrintStatement (dstatement_t *s);
|
||||
type_t *PR_PointerType (type_t *pointsto);
|
||||
int PR_WriteBodylessFuncs (file_t *handle);
|
||||
char *PR_ValueString (etype_t type, void *val);
|
||||
int PR_CopyString (char *str, bool noduplicate );
|
||||
void PR_CompileFile (char *string, char *filename);
|
||||
bool PR_StatementIsAJump(int stnum, int notifdest);
|
||||
void PR_ParsePrintDef (int warningtype, def_t *def);
|
||||
def_t *PR_Expression (int priority, bool allowcomma);
|
||||
void PR_ParseError (int errortype, char *error, ...);
|
||||
int PR_AStatementJumpsTo(int targ, int first, int last);
|
||||
void PR_ParseWarning (int warningtype, char *error, ...);
|
||||
void PR_EmitClassFromFunction(def_t *scope, char *tname);
|
||||
byte *PR_LoadFile(char *filename, bool crash, int type );
|
||||
int PR_encode( int len, int method, char *in, file_t *h );
|
||||
void PR_EmitArrayGetFunction(def_t *scope, char *arrayname);
|
||||
void PR_EmitArraySetFunction(def_t *scope, char *arrayname);
|
||||
type_t *PR_ParseFunctionType (int newtype, type_t *returntype);
|
||||
void PR_IncludeChunk (char *data, bool duplicate, char *filename);
|
||||
void PR_Warning (int type, char *file, int line, char *error, ...);
|
||||
void PR_ParseErrorPrintDef (int errortype, def_t *def, char *error, ...);
|
||||
int PR_WriteSourceFiles(file_t *h, dprograms_t *progs, bool sourceaswell);
|
||||
int PR_decode(int complen, int len, int method, char *info, char **buffer);
|
||||
void PR_WriteAsmFunction(def_t *sc, uint firststatement, gofs_t firstparm);
|
||||
def_t *PR_GetDef (type_t *type, char *name, def_t *scope, bool allocate, int arraysize);
|
||||
def_t *PR_Statement ( opcode_t *op, def_t *var_a, def_t *var_b, dstatement_t **outstatement);
|
||||
void PR_RemapOffsets(uint firststatement, uint laststatement, uint min, uint max, uint newmin);
|
||||
def_t *PR_DummyDef(type_t *type, char *name, def_t *scope, int arraysize, uint ofs, int referable);
|
||||
void PR_BeginCompilation ( void );
|
||||
bool PR_ContinueCompile ( void );
|
||||
void PR_FinishCompilation ( void );
|
||||
|
||||
//
|
||||
// pr_decomp.c
|
||||
//
|
||||
void PR_InitTypes( void );
|
||||
bool PR_Decompile( const char *name );
|
||||
|
||||
#endif//PR_LOCAL_H
|
|
@ -0,0 +1,392 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2007 ©
|
||||
// pr_main.c - PRVM compiler-executor
|
||||
//=======================================================================
|
||||
|
||||
#include "vprogs.h"
|
||||
|
||||
stdlib_api_t com;
|
||||
byte *qccpool;
|
||||
int com_argc = 0;
|
||||
char **com_argv;
|
||||
char v_copyright[1024];
|
||||
int MAX_ERRORS;
|
||||
int numtemps;
|
||||
bool compileactive = false;
|
||||
char progsoutname[MAX_SYSPATH];
|
||||
float *pr_globals;
|
||||
uint numpr_globals;
|
||||
char *strings;
|
||||
int strofs;
|
||||
dstatement_t *statements;
|
||||
int numstatements;
|
||||
int *statement_linenums;
|
||||
char sourcedir[MAX_SYSPATH];
|
||||
cachedsourcefile_t *sourcefile;
|
||||
dfunction_t *functions;
|
||||
int numfunctions;
|
||||
ddef_t *qcc_globals;
|
||||
int numglobaldefs;
|
||||
ddef_t *fields;
|
||||
int numfielddefs;
|
||||
bool pr_warning[WARN_MAX];
|
||||
bool bodylessfuncs;
|
||||
type_t *qcc_typeinfo;
|
||||
int numtypeinfos;
|
||||
int maxtypeinfos;
|
||||
int prvm_developer;
|
||||
int host_instance;
|
||||
int prvm_state;
|
||||
vprogs_exp_t vm;
|
||||
|
||||
hashtable_t compconstantstable;
|
||||
hashtable_t globalstable;
|
||||
hashtable_t localstable;
|
||||
hashtable_t intconstdefstable;
|
||||
hashtable_t floatconstdefstable;
|
||||
hashtable_t stringconstdefstable;
|
||||
|
||||
cvar_t *prvm_maxedicts;
|
||||
cvar_t *prvm_traceqc;
|
||||
cvar_t *prvm_boundscheck;
|
||||
cvar_t *prvm_statementprofiling;
|
||||
|
||||
void PR_SetDefaultProperties( void )
|
||||
{
|
||||
const_t *cnst;
|
||||
int i, j;
|
||||
char *name, *val;
|
||||
|
||||
Hash_InitTable( &compconstantstable, MAX_CONSTANTS );
|
||||
|
||||
ForcedCRC = 0;
|
||||
// enable all warnings
|
||||
Mem_Set(pr_warning, 0, sizeof(pr_warning));
|
||||
|
||||
// reset all optimizarions
|
||||
for( i = 0; pr_optimisations[i].enabled; i++ )
|
||||
*pr_optimisations[i].enabled = false;
|
||||
|
||||
PR_DefineName("_QCLIB"); // compiler type
|
||||
|
||||
// play with default warnings.
|
||||
pr_warning[WARN_NOTREFERENCEDCONST] = true;
|
||||
pr_warning[WARN_MACROINSTRING] = true;
|
||||
pr_warning[WARN_FIXEDRETURNVALUECONFLICT] = true;
|
||||
pr_warning[WARN_EXTRAPRECACHE] = true;
|
||||
pr_warning[WARN_DEADCODE] = true;
|
||||
pr_warning[WARN_INEFFICIENTPLUSPLUS] = true;
|
||||
pr_warning[WARN_EXTENSION_USED] = true;
|
||||
pr_warning[WARN_IFSTRING_USED] = true;
|
||||
|
||||
for (i = 1; i < com_argc; i++)
|
||||
{
|
||||
if( !com.strnicmp(com_argv[i], "/D", 2))
|
||||
{
|
||||
// #define
|
||||
name = com_argv[i+1];
|
||||
val = com.strchr(name, '=');
|
||||
if (val) { *val = '\0', val++; }
|
||||
cnst = PR_DefineName(name);
|
||||
if (val)
|
||||
{
|
||||
if(com.strlen(val) + 1 >= sizeof(cnst->value))
|
||||
PR_ParseError(ERR_INTERNAL, "compiler constant value is too long\n");
|
||||
com.strncpy(cnst->value, val, sizeof(cnst->value)-1);
|
||||
PR_Message("value %s\n", val );
|
||||
cnst->value[sizeof(cnst->value)-1] = '\0';
|
||||
}
|
||||
}
|
||||
else if( !com.strnicmp(com_argv[i], "/O", 2))
|
||||
{
|
||||
int currentlevel = 0; // optimization level
|
||||
if(com_argv[i][2] == 'd') currentlevel = MASK_DEBUG; // disable optimizations
|
||||
else if (com_argv[i][2] == '0') currentlevel = MASK_LEVEL_O;
|
||||
else if (com_argv[i][2] == '1') currentlevel = MASK_LEVEL_1;
|
||||
else if (com_argv[i][2] == '2') currentlevel = MASK_LEVEL_2;
|
||||
else if (com_argv[i][2] == '3') currentlevel = MASK_LEVEL_3;
|
||||
else if (com_argv[i][2] == '4') currentlevel = MASK_LEVEL_4;
|
||||
|
||||
if(currentlevel)
|
||||
{
|
||||
for (j = 0; pr_optimisations[j].enabled; j++)
|
||||
{
|
||||
if(pr_optimisations[j].levelmask & currentlevel)
|
||||
*pr_optimisations[j].enabled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char *abbrev = com_argv[i]+2; // custom optimisation
|
||||
for (j = 0; pr_optimisations[j].enabled; j++)
|
||||
{
|
||||
if(!com.strcmp(pr_optimisations[j].shortname, abbrev))
|
||||
{
|
||||
*pr_optimisations[j].enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
PR_Init
|
||||
|
||||
initialize compiler and hash tables
|
||||
===================
|
||||
*/
|
||||
void PR_InitCompile( const char *name )
|
||||
{
|
||||
static char parmname[12][MAX_PARMS];
|
||||
static temp_t ret_temp;
|
||||
int i;
|
||||
|
||||
com.strncat(v_copyright,"This file was created with Xash3D QuakeC compiler,\n", sizeof(v_copyright));
|
||||
com.strncat(v_copyright,"who based on original code of ForeThought's QuakeC compiler.\n",sizeof(v_copyright));
|
||||
com.strncat(v_copyright,"Thanks to ID Software at all.", sizeof(v_copyright));
|
||||
MAX_ERRORS = 10; // per one file
|
||||
maxtypeinfos = 16384;
|
||||
|
||||
PR_SetDefaultProperties();
|
||||
|
||||
numtemps = 0;
|
||||
functemps = NULL;
|
||||
sourcefile = NULL;
|
||||
|
||||
strings = (void *)Qalloc(sizeof(char) * MAX_STRINGS);
|
||||
strofs = 1;
|
||||
|
||||
statements = (void *)Qalloc(sizeof(dstatement_t) * MAX_STATEMENTS);
|
||||
numstatements = 1;
|
||||
|
||||
statement_linenums = (void *)Qalloc(sizeof(int) * MAX_STATEMENTS);
|
||||
|
||||
functions = (void *)Qalloc(sizeof(dfunction_t) * MAX_FUNCTIONS);
|
||||
numfunctions = 1;
|
||||
|
||||
pr_bracelevel = 0;
|
||||
|
||||
pr_globals = (void *)Qalloc(sizeof(float) * MAX_REGS);
|
||||
numpr_globals = 0;
|
||||
|
||||
Hash_InitTable(&globalstable, MAX_REGS);
|
||||
Hash_InitTable(&localstable, MAX_REGS);
|
||||
Hash_InitTable(&floatconstdefstable, MAX_REGS+1);
|
||||
Hash_InitTable(&intconstdefstable, MAX_REGS+1);
|
||||
Hash_InitTable(&stringconstdefstable, MAX_REGS);
|
||||
|
||||
qcc_globals = (void *)Qalloc(sizeof(ddef_t) * MAX_GLOBALS);
|
||||
numglobaldefs = 1;
|
||||
|
||||
fields = (void *)Qalloc(sizeof(ddef_t) * MAX_FIELDS);
|
||||
numfielddefs = 1;
|
||||
|
||||
qcc_typeinfo = (void *)Qalloc(sizeof(type_t) * maxtypeinfos);
|
||||
numtypeinfos = 0;
|
||||
bodylessfuncs = 0;
|
||||
|
||||
Mem_Set(&pr, 0, sizeof(pr));
|
||||
Mem_Set(&ret_temp, 0, sizeof(ret_temp));
|
||||
Mem_Set(pr_immediate_string, 0, sizeof(pr_immediate_string));
|
||||
|
||||
if (opt_locals_marshalling) MsgDev( D_INFO, "Locals marshalling might be buggy. Use with caution\n");
|
||||
com.strncpy( sourcefilename, name, sizeof(sourcefilename));
|
||||
|
||||
// default parms
|
||||
def_ret.ofs = OFS_RETURN;
|
||||
def_ret.name = "return";
|
||||
def_ret.temp = &ret_temp;
|
||||
def_ret.constant = false;
|
||||
def_ret.type = NULL;
|
||||
ret_temp.ofs = def_ret.ofs;
|
||||
ret_temp.scope = NULL;
|
||||
ret_temp.size = 3;
|
||||
ret_temp.next = NULL;
|
||||
|
||||
for (i = 0; i < MAX_PARMS; i++)
|
||||
{
|
||||
def_parms[i].temp = NULL;
|
||||
def_parms[i].type = NULL;
|
||||
def_parms[i].ofs = OFS_PARM0 + 3*i;
|
||||
def_parms[i].name = parmname[i];
|
||||
com.sprintf(parmname[i], "parm%i", i);
|
||||
}
|
||||
}
|
||||
|
||||
void PRVM_Init( int argc, char **argv )
|
||||
{
|
||||
char dev_level[4];
|
||||
|
||||
com_argc = argc;
|
||||
com_argv = argv;
|
||||
|
||||
qccpool = Mem_AllocPool( "VM progs" );
|
||||
host_instance = g_Instance;
|
||||
|
||||
if(FS_GetParmFromCmdLine("-dev", dev_level ))
|
||||
prvm_developer = com.atoi(dev_level);
|
||||
|
||||
Cmd_AddCommand("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_edicts", PRVM_ED_PrintEdicts_f, "set a property on an entity number in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
|
||||
Cmd_AddCommand("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, uimenu)");
|
||||
Cmd_AddCommand("compile", PRVM_Compile_f, "compile specified VM (server, client, menu), changes will take affect after map restart");
|
||||
|
||||
// LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
|
||||
prvm_boundscheck = Cvar_Get( "prvm_boundscheck", "0", 0, "enable vm internal boundschecker" );
|
||||
prvm_traceqc = Cvar_Get( "prvm_traceqc", "0", 0, "enable tracing (only for debug)" );
|
||||
prvm_statementprofiling = Cvar_Get ("prvm_statementprofiling", "0", 0, "counts how many times each QC statement has been executed" );
|
||||
prvm_maxedicts = Cvar_Get( "host_maxedicts", "2048", CVAR_SYSTEMINFO, "user limit edicts number fof server, client and renderer, absolute limit 65535" );
|
||||
|
||||
if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED )
|
||||
{
|
||||
size_t size;
|
||||
byte *image;
|
||||
|
||||
// FIXME: get rid of this
|
||||
// dump internal copies of progs into hdd if missing
|
||||
if(!FS_FileExists(va("%s/uimenu.dat", GI->vprogs_dir)))
|
||||
{
|
||||
image = FS_LoadInternal( "uimenu.dat", &size );
|
||||
if( size ) FS_WriteFile(va("%s/uimenu.dat", GI->vprogs_dir), image, size );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PRVM_Shutdown( void )
|
||||
{
|
||||
Mem_FreePool( &qccpool );
|
||||
}
|
||||
|
||||
void PRVM_PrepareProgs( const char *dir, const char *name )
|
||||
{
|
||||
int i;
|
||||
|
||||
switch( host_instance )
|
||||
{
|
||||
case HOST_QCCLIB:
|
||||
FS_InitRootDir((char *)dir);
|
||||
PR_InitCompile( name );
|
||||
break;
|
||||
case HOST_NORMAL:
|
||||
case HOST_DEDICATED:
|
||||
com_argc = Cmd_Argc();
|
||||
for( i = 0; i < com_argc; i++ )
|
||||
com_argv[i] = copystring(Cmd_Argv(i));
|
||||
com.strncpy( sourcedir, name, MAX_SYSPATH );
|
||||
PR_InitCompile( name );
|
||||
prvm_state = comp_begin;
|
||||
break;
|
||||
default:
|
||||
Sys_Break("PRVM_PrepareProgs: can't prepare progs for instance %d\n", host_instance );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PRVM_CompileProgs( void )
|
||||
{
|
||||
PR_BeginCompilation();
|
||||
while(PR_ContinueCompile());
|
||||
PR_FinishCompilation();
|
||||
}
|
||||
|
||||
void PRVM_Frame( double time )
|
||||
{
|
||||
if( setjmp( pr_int_error ))
|
||||
return;
|
||||
|
||||
switch( prvm_state )
|
||||
{
|
||||
case comp_begin:
|
||||
PR_BeginCompilation();
|
||||
prvm_state = comp_frame;
|
||||
break;
|
||||
case comp_frame:
|
||||
if(PR_ContinueCompile());
|
||||
else prvm_state = comp_done;
|
||||
break;
|
||||
case comp_done:
|
||||
prvm_state = comp_inactive;
|
||||
PR_FinishCompilation();
|
||||
break;
|
||||
case comp_error:
|
||||
prvm_state = comp_inactive;
|
||||
break;
|
||||
case comp_inactive:
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
void PRVM_Compile_f( void )
|
||||
{
|
||||
if( Cmd_Argc() < 2)
|
||||
{
|
||||
Msg( "Usage: compile <program name> </D=DEFINE> </O<level>> </O<abbrev>>\n");
|
||||
return;
|
||||
}
|
||||
if( prvm_state != comp_inactive )
|
||||
{
|
||||
Msg( "Compile already in progress, please wait\n" );
|
||||
return;
|
||||
}
|
||||
// engine already known about vprogs and vsource directory
|
||||
PRVM_PrepareProgs( NULL, Cmd_Argv( 1 ));
|
||||
}
|
||||
|
||||
vprogs_exp_t DLLEXPORT *CreateAPI( stdlib_api_t *input, void *unused )
|
||||
{
|
||||
com = *input;
|
||||
|
||||
// generic functions
|
||||
vm.api_size = sizeof(vprogs_exp_t);
|
||||
|
||||
vm.Init = PRVM_Init;
|
||||
vm.Free = PRVM_Shutdown;
|
||||
vm.PrepareDAT = PRVM_PrepareProgs;
|
||||
vm.CompileDAT = PRVM_CompileProgs;
|
||||
vm.Update = PRVM_Frame;
|
||||
|
||||
vm.WriteGlobals = PRVM_ED_WriteGlobals;
|
||||
vm.ReadGlobals = PRVM_ED_ReadGlobals;
|
||||
vm.PrintEdict = PRVM_ED_Print;
|
||||
vm.WriteEdict = PRVM_ED_Write;
|
||||
vm.ReadEdict = PRVM_ED_Read;
|
||||
vm.AllocEdict = PRVM_ED_Alloc;
|
||||
vm.FreeEdict = PRVM_ED_Free;
|
||||
|
||||
vm.LoadFromFile = PRVM_ED_LoadFromFile;
|
||||
vm.GetString = PRVM_GetString;
|
||||
vm.SetEngineString = PRVM_SetEngineString;
|
||||
vm.SetTempString = PRVM_SetTempString;
|
||||
|
||||
vm.InitProg = PRVM_InitProg;
|
||||
vm.SetProg = PRVM_SetProg;
|
||||
vm.ProgLoaded = PRVM_ProgLoaded;
|
||||
vm.LoadProgs = PRVM_LoadProgs;
|
||||
vm.ResetProg = PRVM_ResetProg;
|
||||
|
||||
vm.FindFunctionOffset = PRVM_ED_FindFunctionOffset;
|
||||
vm.FindGlobalOffset = PRVM_ED_FindGlobalOffset;
|
||||
vm.FindFieldOffset = PRVM_ED_FindFieldOffset;
|
||||
vm.FindFunction = PRVM_ED_FindFunction;
|
||||
vm.FindGlobal = PRVM_ED_FindGlobal;
|
||||
vm.FindField = PRVM_ED_FindField;
|
||||
|
||||
vm.StackTrace = PRVM_StackTrace;
|
||||
vm.Warning = VM_Warning;
|
||||
vm.Error = VM_Error;
|
||||
vm.ExecuteProgram = PRVM_ExecuteProgram;
|
||||
vm.Crash = PRVM_Crash;
|
||||
|
||||
return &vm;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,153 @@
|
|||
# Microsoft Developer Studio Project File - Name="vprogs" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=vprogs - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "vprogs.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "vprogs.mak" CFG="vprogs - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "vprogs - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "vprogs - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "vprogs - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "..\temp\vprogs\!release"
|
||||
# PROP Intermediate_Dir "..\temp\vprogs\!release"
|
||||
# PROP Ignore_Export_Lib 1
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PLATFORM_EXPORTS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /O2 /I "./" /I "../public" /I "../common" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x419 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /opt:nowin98
|
||||
# ADD LINK32 msvcrt.lib /nologo /dll /pdb:none /machine:I386 /nodefaultlib:"libc.lib" /opt:nowin98
|
||||
# SUBTRACT LINK32 /profile
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\vprogs\!release
|
||||
InputPath=\Xash3D\src_main\temp\vprogs\!release\vprogs.dll
|
||||
SOURCE="$(InputPath)"
|
||||
|
||||
"D:\Xash3D\bin\vprogs.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
|
||||
copy $(TargetDir)\vprogs.dll "D:\Xash3D\bin\vprogs.dll"
|
||||
|
||||
# End Custom Build
|
||||
|
||||
!ELSEIF "$(CFG)" == "vprogs - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "..\temp\vprogs\!debug"
|
||||
# PROP Intermediate_Dir "..\temp\vprogs\!debug"
|
||||
# PROP Ignore_Export_Lib 1
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PLATFORM_EXPORTS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x419 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 msvcrtd.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
# SUBTRACT LINK32 /incremental:no /nodefaultlib
|
||||
# Begin Custom Build
|
||||
TargetDir=\Xash3D\src_main\temp\vprogs\!debug
|
||||
InputPath=\Xash3D\src_main\temp\vprogs\!debug\vprogs.dll
|
||||
SOURCE="$(InputPath)"
|
||||
|
||||
"D:\Xash3D\bin\vprogs.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
|
||||
copy $(TargetDir)\vprogs.dll "D:\Xash3D\bin\vprogs.dll"
|
||||
|
||||
# End Custom Build
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "vprogs - Win32 Release"
|
||||
# Name "vprogs - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_comp.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_edict.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_exec.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_lex.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_main.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_utils.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pr_local.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\vprogs.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
|
@ -0,0 +1,467 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2007 ©
|
||||
// vprogs.h - virtual machine export
|
||||
//=======================================================================
|
||||
#ifndef VPROGS_H
|
||||
#define VPROGS_H
|
||||
|
||||
#include <windows.h>
|
||||
#include "launch_api.h"
|
||||
#include "qfiles_ref.h"
|
||||
#include "vprogs_api.h"
|
||||
#include "pr_local.h"
|
||||
|
||||
extern stdlib_api_t com;
|
||||
extern vprogs_exp_t vm;
|
||||
|
||||
extern cvar_t *prvm_traceqc;
|
||||
extern cvar_t *prvm_boundscheck;
|
||||
extern cvar_t *prvm_statementprofiling;
|
||||
extern int prvm_developer;
|
||||
|
||||
#define Host_Error com.error
|
||||
#define PRVM_MAX_STACK_DEPTH 1024
|
||||
#define PRVM_LOCALSTACK_SIZE 16384
|
||||
#define PRVM_MAX_OPENFILES 256
|
||||
#define PRVM_MAX_OPENSEARCHES 128
|
||||
|
||||
enum op_state
|
||||
{
|
||||
OP_DONE, // 0
|
||||
OP_MUL_F,
|
||||
OP_MUL_V,
|
||||
OP_MUL_FV, // (vec3_t) * (float)
|
||||
OP_MUL_VF, // (float) * (vec3_t)
|
||||
OP_DIV_F,
|
||||
OP_ADD_F,
|
||||
OP_ADD_V,
|
||||
OP_SUB_F,
|
||||
OP_SUB_V,
|
||||
|
||||
OP_EQ_F, // 10
|
||||
OP_EQ_V,
|
||||
OP_EQ_S,
|
||||
OP_EQ_E,
|
||||
OP_EQ_FNC,
|
||||
|
||||
OP_NE_F,
|
||||
OP_NE_V,
|
||||
OP_NE_S,
|
||||
OP_NE_E,
|
||||
OP_NE_FNC,
|
||||
|
||||
OP_LE, // = (float) <= (float);
|
||||
OP_GE, // = (float) >= (float);
|
||||
OP_LT, // = (float) < (float);
|
||||
OP_GT, // = (float) > (float);
|
||||
|
||||
OP_LOAD_F,
|
||||
OP_LOAD_V,
|
||||
OP_LOAD_S,
|
||||
OP_LOAD_ENT,
|
||||
OP_LOAD_FLD,
|
||||
OP_LOAD_FNC,
|
||||
|
||||
OP_ADDRESS, // 30
|
||||
|
||||
OP_STORE_F,
|
||||
OP_STORE_V,
|
||||
OP_STORE_S,
|
||||
OP_STORE_ENT,
|
||||
OP_STORE_FLD,
|
||||
OP_STORE_FNC,
|
||||
|
||||
OP_STOREP_F,
|
||||
OP_STOREP_V,
|
||||
OP_STOREP_S,
|
||||
OP_STOREP_ENT, // 40
|
||||
OP_STOREP_FLD,
|
||||
OP_STOREP_FNC,
|
||||
|
||||
OP_RETURN,
|
||||
OP_NOT_F,
|
||||
OP_NOT_V,
|
||||
OP_NOT_S,
|
||||
OP_NOT_ENT,
|
||||
OP_NOT_FNC,
|
||||
OP_NOT_BITI,
|
||||
OP_NOT_BITF,
|
||||
OP_IF,
|
||||
OP_IFNOT, // 50
|
||||
OP_CALL0,
|
||||
OP_CALL1,
|
||||
OP_CALL2,
|
||||
OP_CALL3,
|
||||
OP_CALL4,
|
||||
OP_CALL5,
|
||||
OP_CALL6,
|
||||
OP_CALL7,
|
||||
OP_CALL8,
|
||||
OP_CALL9,
|
||||
OP_STATE, // 60
|
||||
OP_GOTO,
|
||||
OP_AND,
|
||||
OP_OR,
|
||||
|
||||
OP_BITAND, // = (float) & (float); // of cource converting into integer in real code
|
||||
OP_BITOR,
|
||||
|
||||
OP_MULSTORE_F, // f *= f
|
||||
OP_MULSTORE_V, // v *= f
|
||||
OP_MULSTOREP_F, // e.f *= f
|
||||
OP_MULSTOREP_V, // e.v *= f
|
||||
|
||||
OP_DIVSTORE_F, // f /= f
|
||||
OP_DIVSTOREP_F, // e.f /= f
|
||||
|
||||
OP_ADDSTORE_F, // f += f
|
||||
OP_ADDSTORE_V, // v += v
|
||||
OP_ADDSTOREP_F, // e.f += f
|
||||
OP_ADDSTOREP_V, // e.v += v
|
||||
|
||||
OP_SUBSTORE_F, // f -= f
|
||||
OP_SUBSTORE_V, // v -= v
|
||||
OP_SUBSTOREP_F, // e.f -= f
|
||||
OP_SUBSTOREP_V, // e.v -= v
|
||||
|
||||
OP_FETCH_GBL_F, // 80
|
||||
OP_FETCH_GBL_V,
|
||||
OP_FETCH_GBL_S,
|
||||
OP_FETCH_GBL_E,
|
||||
OP_FETCH_G_FNC,
|
||||
|
||||
OP_CSTATE,
|
||||
OP_CWSTATE,
|
||||
|
||||
OP_THINKTIME,
|
||||
|
||||
OP_BITSET, // b (+) a
|
||||
OP_BITSETP, // .b (+) a
|
||||
OP_BITCLR, // b (-) a
|
||||
OP_BITCLRP, // .b (-) a
|
||||
|
||||
OP_RAND0,
|
||||
OP_RAND1,
|
||||
OP_RAND2,
|
||||
OP_RANDV0,
|
||||
OP_RANDV1,
|
||||
OP_RANDV2,
|
||||
|
||||
OP_SWITCH_F, // switches
|
||||
OP_SWITCH_V,
|
||||
OP_SWITCH_S, // 100
|
||||
OP_SWITCH_E,
|
||||
OP_SWITCH_FNC,
|
||||
|
||||
OP_CASE,
|
||||
OP_CASERANGE,
|
||||
|
||||
OP_STORE_I,
|
||||
OP_STORE_IF,
|
||||
OP_STORE_FI,
|
||||
|
||||
OP_ADD_I,
|
||||
OP_ADD_FI,
|
||||
OP_ADD_IF, // 110
|
||||
|
||||
OP_SUB_I,
|
||||
OP_SUB_FI,
|
||||
OP_SUB_IF,
|
||||
|
||||
OP_CONV_ITOF,
|
||||
OP_CONV_FTOI,
|
||||
OP_CP_ITOF,
|
||||
OP_CP_FTOI,
|
||||
OP_LOAD_I,
|
||||
OP_STOREP_I,
|
||||
OP_STOREP_IF, // 120
|
||||
OP_STOREP_FI,
|
||||
|
||||
OP_BITAND_I,
|
||||
OP_BITOR_I,
|
||||
|
||||
OP_MUL_I,
|
||||
OP_DIV_I,
|
||||
OP_EQ_I,
|
||||
OP_NE_I,
|
||||
|
||||
OP_IFNOTS,
|
||||
OP_IFS,
|
||||
|
||||
OP_NOT_I, // 130
|
||||
|
||||
OP_DIV_VF,
|
||||
|
||||
OP_POWER_I,
|
||||
OP_RSHIFT_I,
|
||||
OP_LSHIFT_I,
|
||||
OP_RSHIFT_F,
|
||||
OP_LSHIFT_F,
|
||||
OP_MODULO_I, // (int)c = (int)a % (int)b
|
||||
OP_MODULO_F, // (float)c = fmod( (float)a, (float)b )
|
||||
|
||||
OP_GLOBAL_ADD,
|
||||
OP_POINTER_ADD, // pointer to 32 bit (remember to *3 for vectors)
|
||||
|
||||
OP_LOADA_F,
|
||||
OP_LOADA_V,
|
||||
OP_LOADA_S,
|
||||
OP_LOADA_ENT, // 140
|
||||
OP_LOADA_FLD,
|
||||
OP_LOADA_FNC,
|
||||
OP_LOADA_I,
|
||||
|
||||
OP_STORE_P,
|
||||
OP_LOAD_P,
|
||||
|
||||
OP_LOADP_F,
|
||||
OP_LOADP_V,
|
||||
OP_LOADP_S,
|
||||
OP_LOADP_ENT,
|
||||
OP_LOADP_FLD, // 150
|
||||
OP_LOADP_FNC,
|
||||
OP_LOADP_I,
|
||||
|
||||
OP_LE_I, // (int)c = (int)a <= (int)b;
|
||||
OP_GE_I, // (int)c = (int)a >= (int)b;
|
||||
OP_LT_I, // (int)c = (int)a < (int)b;
|
||||
OP_GT_I, // (int)c = (int)a > (int)b;
|
||||
|
||||
OP_LE_IF, // (float)c = (int)a <= (float)b;
|
||||
OP_GE_IF, // (float)c = (int)a >= (float)b;
|
||||
OP_LT_IF, // (float)c = (int)a < (float)b;
|
||||
OP_GT_IF, // (float)c = (int)a > (float)b;
|
||||
|
||||
OP_LE_FI, // (float)c = (float)a <= (int)b;
|
||||
OP_GE_FI, // (float)c = (float)a >= (int)b;
|
||||
OP_LT_FI, // (float)c = (float)a < (int)b;
|
||||
OP_GT_FI, // (float)c = (float)a > (int)b;
|
||||
|
||||
OP_EQ_IF,
|
||||
OP_EQ_FI,
|
||||
|
||||
OP_ADD_SF, // (char*)c = (char*)a + (float)b
|
||||
OP_SUB_S, // (float)c = (char*)a - (char*)b
|
||||
OP_STOREP_C, // (float)c = *(char*)b = (float)a
|
||||
OP_LOADP_C, // (float)c = *(char*) // 170
|
||||
|
||||
OP_MUL_IF,
|
||||
OP_MUL_FI,
|
||||
OP_MUL_VI,
|
||||
OP_MUL_IV,
|
||||
OP_DIV_IF,
|
||||
OP_DIV_FI,
|
||||
OP_BITAND_IF,
|
||||
OP_BITOR_IF,
|
||||
OP_BITAND_FI,
|
||||
OP_BITOR_FI, // 180
|
||||
OP_AND_I,
|
||||
OP_OR_I,
|
||||
OP_AND_IF,
|
||||
OP_OR_IF,
|
||||
OP_AND_FI,
|
||||
OP_OR_FI,
|
||||
OP_NE_IF,
|
||||
OP_NE_FI,
|
||||
|
||||
OP_GSTOREP_I,
|
||||
OP_GSTOREP_F, // 190
|
||||
OP_GSTOREP_ENT,
|
||||
OP_GSTOREP_FLD, // integers
|
||||
OP_GSTOREP_S,
|
||||
OP_GSTOREP_FNC, // pointers
|
||||
OP_GSTOREP_V,
|
||||
OP_GADDRESS,
|
||||
OP_GLOAD_I,
|
||||
OP_GLOAD_F,
|
||||
OP_GLOAD_FLD,
|
||||
OP_GLOAD_ENT, // 200
|
||||
OP_GLOAD_S,
|
||||
OP_GLOAD_FNC,
|
||||
OP_GLOAD_V,
|
||||
OP_BOUNDCHECK,
|
||||
|
||||
OP_STOREP_P, // back to ones that we do use.
|
||||
OP_PUSH,
|
||||
OP_POP,
|
||||
|
||||
OP_NUMOPS,
|
||||
};
|
||||
|
||||
#define PRVM_EDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (prvm_eval_t *)((byte *)ed->progs.vp + fieldoffset) : NULL)
|
||||
#define PRVM_GLOBALFIELDVALUE(fieldoffset) (fieldoffset ? (prvm_eval_t *)((byte *)vm.prog->globals.gp + fieldoffset) : NULL)
|
||||
|
||||
extern prvm_prog_t prvm_prog_list[PRVM_MAXPROGS];
|
||||
|
||||
//============================================================================
|
||||
// prvm_cmds part
|
||||
|
||||
extern prvm_builtin_t vm_sv_builtins[];
|
||||
extern prvm_builtin_t vm_cl_builtins[];
|
||||
extern prvm_builtin_t vm_m_builtins[];
|
||||
|
||||
extern const int vm_sv_numbuiltins;
|
||||
extern const int vm_cl_numbuiltins;
|
||||
extern const int vm_m_numbuiltins;
|
||||
|
||||
extern char *vm_sv_extensions;
|
||||
extern char *vm_cl_extensions;
|
||||
extern char *vm_m_extensions;
|
||||
|
||||
void VM_SV_Cmd_Init(void);
|
||||
void VM_SV_Cmd_Reset(void);
|
||||
|
||||
void VM_CL_Cmd_Init(void);
|
||||
void VM_CL_Cmd_Reset(void);
|
||||
|
||||
void VM_M_Cmd_Init(void);
|
||||
void VM_M_Cmd_Reset(void);
|
||||
|
||||
void VM_Cmd_Init(void);
|
||||
void VM_Cmd_Reset(void);
|
||||
//============================================================================
|
||||
|
||||
// console commands
|
||||
void PRVM_ED_PrintEdicts_f( void );
|
||||
void PRVM_ED_PrintEdict_f( void );
|
||||
void PRVM_ED_EdictSet_f( void );
|
||||
void PRVM_GlobalSet_f( void );
|
||||
void PRVM_Decompile_f( void );
|
||||
void PRVM_ED_Count_f( void );
|
||||
void PRVM_Globals_f( void );
|
||||
void PRVM_Compile_f( void );
|
||||
void PRVM_Global_f( void );
|
||||
void PRVM_Fields_f( void );
|
||||
|
||||
void PRVM_ExecuteProgram( func_t fnum, const char *name, const char *file, const int line );
|
||||
|
||||
#define PRVM_Alloc(buffersize) _PRVM_Alloc(buffersize, __FILE__, __LINE__)
|
||||
#define PRVM_Free(buffer) _PRVM_Free(buffer, __FILE__, __LINE__)
|
||||
#define PRVM_FreeAll() _PRVM_FreeAll(__FILE__, __LINE__)
|
||||
void *_PRVM_Alloc (size_t buffersize, const char *filename, int fileline);
|
||||
void _PRVM_Free (void *buffer, const char *filename, int fileline);
|
||||
void _PRVM_FreeAll (const char *filename, int fileline);
|
||||
|
||||
void PRVM_Profile (int maxfunctions, int mininstructions);
|
||||
void PRVM_Profile_f (void);
|
||||
void PRVM_PrintFunction_f (void);
|
||||
|
||||
void PRVM_PrintState(void);
|
||||
void PRVM_CrashAll (void);
|
||||
void PRVM_Crash (void);
|
||||
|
||||
int PRVM_ED_FindFieldOffset(const char *field);
|
||||
int PRVM_ED_FindGlobalOffset(const char *global);
|
||||
ddef_t *PRVM_ED_FindField (const char *name);
|
||||
ddef_t *PRVM_ED_FindGlobal (const char *name);
|
||||
mfunction_t *PRVM_ED_FindFunction (const char *name);
|
||||
func_t PRVM_ED_FindFunctionOffset(const char *function);
|
||||
|
||||
void PRVM_MEM_IncreaseEdicts(void);
|
||||
|
||||
pr_edict_t *PRVM_ED_Alloc (void);
|
||||
void PRVM_ED_Free (pr_edict_t *ed);
|
||||
void PRVM_ED_ClearEdict (pr_edict_t *e);
|
||||
ddef_t *PRVM_ED_GlobalAtOfs( int ofs );
|
||||
void PRVM_PrintFunctionStatements (const char *name);
|
||||
void PRVM_ED_Print(pr_edict_t *ed);
|
||||
void PRVM_ED_Write( pr_edict_t *ed, void *buffer, void *ptr, setpair_t callback );
|
||||
void PRVM_ED_Read( int s_table, int ednum, dkeyvalue_t *fields, int numpairs );
|
||||
const char *PRVM_ED_ParseEdict (const char *data, pr_edict_t *ent);
|
||||
char *PRVM_ValueString( etype_t type, prvm_eval_t *val );
|
||||
void PRVM_ED_WriteGlobals( void *buffer, void *ptr, setpair_t callback );
|
||||
void PRVM_ED_ReadGlobals( int s_table, dkeyvalue_t *globals, int numpairs );
|
||||
|
||||
void PRVM_ED_LoadFromFile( const char *data );
|
||||
|
||||
pr_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline);
|
||||
#define PRVM_EDICT_NUM(n) (((n) >= 0 && (n) < vm.prog->max_edicts) ? vm.prog->edicts + (n) : PRVM_EDICT_NUM_ERROR(n, __FILE__, __LINE__))
|
||||
#define PRVM_EDICT_NUM_UNSIGNED(n) (((n) < vm.prog->max_edicts) ? vm.prog->edicts + (n) : PRVM_EDICT_NUM_ERROR(n, __FILE__, __LINE__))
|
||||
#define PRVM_NUM_FOR_EDICT(e) ((int)((pr_edict_t *)(e) - vm.prog->edicts))
|
||||
#define PRVM_NEXT_EDICT(e) ((e) + 1)
|
||||
#define PRVM_EDICT_TO_PROG(e) (PRVM_NUM_FOR_EDICT(e))
|
||||
#define PRVM_PROG_TO_EDICT(n) (PRVM_EDICT_NUM(n))
|
||||
#define PRVM_ED_POINTER(p) (prvm_eval_t *)((byte *)vm.prog->edictsfields + p->_int)
|
||||
#define PRVM_EM_POINTER(p) (prvm_eval_t *)((byte *)vm.prog->edictsfields + (p))
|
||||
#define PRVM_EV_POINTER(p) (prvm_eval_t *)(((byte *)vm.prog->edicts) + p->_int) // this is correct ???
|
||||
#define PRVM_CHECK_PTR(p, size) if(prvm_boundscheck->value && (p->_int < 0 || p->_int + size > vm.prog->edictareasize))\
|
||||
{\
|
||||
vm.prog->xfunction->profile += (st - startst);\
|
||||
vm.prog->xstatement = st - vm.prog->statements;\
|
||||
PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, p->_int);\
|
||||
return;\
|
||||
}
|
||||
#define PRVM_CHECK_INFINITE() if (++jumpcount == 10000000)\
|
||||
{\
|
||||
vm.prog->xstatement = st - vm.prog->statements;\
|
||||
PRVM_Profile(1<<30, 1000000);\
|
||||
PRVM_ERROR("runaway loop counter hit limit of %d jumps\n", jumpcount, PRVM_NAME);\
|
||||
}
|
||||
//============================================================================
|
||||
|
||||
// get arguments from progs
|
||||
#define PRVM_G_FLOAT(o) (vm.prog->globals.gp[o])
|
||||
#define PRVM_G_INT(o) (*(int *)&vm.prog->globals.gp[o])
|
||||
#define PRVM_G_EDICT(o) (PRVM_PROG_TO_EDICT(*(uint *)&vm.prog->globals.gp[o]))
|
||||
#define PRVM_G_EDICTNUM(o) PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(o))
|
||||
#define PRVM_G_VECTOR(o) (&vm.prog->globals.gp[o])
|
||||
#define PRVM_G_STRING(o) (PRVM_GetString(*(string_t *)&vm.prog->globals.gp[o]))
|
||||
#define PRVM_G_FUNCTION(o) (*(func_t *)&vm.prog->globals.gp[o])
|
||||
|
||||
// FIXME: make these go away?
|
||||
#define PRVM_E_FLOAT(e,o) (((float*)e->progs.vp)[o])
|
||||
#define PRVM_E_INT(e,o) (((int*)e->progs.vp)[o])
|
||||
//#define PRVM_E_VECTOR(e,o) (&((float*)e->progs.vp)[o])
|
||||
#define PRVM_E_STRING(e,o) (PRVM_GetString(*(string_t *)&((float*)e->progs.vp)[o]))
|
||||
|
||||
extern int prvm_type_size[8]; // for consistency : I think a goal of this sub-project is to
|
||||
// make the new vm mostly independent from the old one, thus if it's necessary, I copy everything
|
||||
|
||||
void PRVM_Init_Exec(void);
|
||||
void PRVM_StackTrace (void);
|
||||
void PRVM_ED_PrintEdicts_f (void);
|
||||
void PRVM_ED_PrintNum (int ent);
|
||||
|
||||
const char *PRVM_GetString(int num);
|
||||
int PRVM_SetEngineString(const char *s);
|
||||
int PRVM_SetTempString( const char *s );
|
||||
int PRVM_AllocString(size_t bufferlength, char **pointer);
|
||||
void PRVM_FreeString(int num);
|
||||
|
||||
//============================================================================
|
||||
#define PRVM_NAME (vm.prog->name ? vm.prog->name : "unnamed.dat")
|
||||
|
||||
// helper macro to make function pointer calls easier
|
||||
#define PRVM_GCALL(func) if(vm.prog->func) vm.prog->func
|
||||
#define PRVM_ERROR if(vm.prog) vm.prog->error_cmd
|
||||
|
||||
// other prog handling functions
|
||||
bool PRVM_SetProgFromString(const char *str);
|
||||
void PRVM_SetProg(int prognr);
|
||||
|
||||
/*
|
||||
Initializing a vm:
|
||||
Call InitProg with the num
|
||||
Set up the fields marked with [INIT] in the prog struct
|
||||
Load a program with LoadProgs
|
||||
*/
|
||||
void PRVM_InitProg(int prognr);
|
||||
|
||||
// LoadProgs expects to be called right after InitProg
|
||||
void PRVM_LoadProgs( const char *filename );
|
||||
void PRVM_ResetProg( void );
|
||||
|
||||
bool PRVM_ProgLoaded(int prognr);
|
||||
|
||||
int PRVM_GetProgNr(void);
|
||||
|
||||
void VM_Warning(const char *fmt, ...);
|
||||
void VM_Error(const char *fmt, ...);
|
||||
|
||||
// TODO: fill in the params
|
||||
//void PRVM_Create();
|
||||
|
||||
//============================================================================
|
||||
// nice helper macros
|
||||
|
||||
#endif//VPROGS_H
|
Binary file not shown.
|
@ -0,0 +1,51 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2007 ©
|
||||
// s_export.c - sound library main
|
||||
//=======================================================================
|
||||
|
||||
#include "sound.h"
|
||||
|
||||
vsound_imp_t si;
|
||||
stdlib_api_t com;
|
||||
byte *sndpool;
|
||||
|
||||
vsound_exp_t DLLEXPORT *CreateAPI( stdlib_api_t *input, vsound_imp_t *engfuncs )
|
||||
{
|
||||
static vsound_exp_t snd;
|
||||
|
||||
com = *input;
|
||||
|
||||
// Sys_LoadLibrary can create fake instance, to check
|
||||
// api version and api size, but second argument will be 0
|
||||
// and always make exception, run simply check for avoid it
|
||||
if(engfuncs) si = *engfuncs;
|
||||
|
||||
// generic functions
|
||||
snd.api_size = sizeof(vsound_exp_t);
|
||||
|
||||
snd.Init = S_Init;
|
||||
snd.Shutdown = S_Shutdown;
|
||||
|
||||
// sound manager
|
||||
snd.BeginRegistration = S_BeginRegistration;
|
||||
snd.RegisterSound = S_RegisterSound;
|
||||
snd.EndRegistration = S_EndRegistration;
|
||||
|
||||
snd.StartSound = S_StartSound;
|
||||
snd.StreamRawSamples = S_StreamRawSamples;
|
||||
snd.AddLoopingSound = S_AddLoopingSound;
|
||||
snd.StartLocalSound = S_StartLocalSound;
|
||||
snd.StartBackgroundTrack = S_StartBackgroundTrack;
|
||||
snd.StopBackgroundTrack = S_StopBackgroundTrack;
|
||||
|
||||
snd.StartStreaming = S_StartStreaming;
|
||||
snd.StopStreaming = S_StopStreaming;
|
||||
|
||||
snd.Frame = S_Update;
|
||||
snd.StopAllSounds = S_StopAllSounds;
|
||||
snd.FreeSounds = S_FreeSounds;
|
||||
|
||||
snd.Activate = S_Activate;
|
||||
|
||||
return &snd;
|
||||
}
|
|
@ -0,0 +1,710 @@
|
|||
//=======================================================================
|
||||
// Copyright XashXT Group 2007 ©
|
||||
// s_load.c - sound managment
|
||||
//=======================================================================
|
||||
|
||||
#include "sound.h"
|
||||
#include "s_stream.h"
|
||||
|
||||
#define MAX_SFX 4096
|
||||
static sfx_t s_knownSfx[MAX_SFX];
|
||||
static int s_numSfx = 0;
|
||||
int s_registration_sequence = 0;
|
||||
bool s_registering = false;
|
||||
|
||||
typedef struct loadformat_s
|
||||
{
|
||||
char *formatstring;
|
||||
char *ext;
|
||||
bool (*loadfunc)( const char *name, byte **wav, wavinfo_t *info );
|
||||
} loadformat_t;
|
||||
|
||||
/*
|
||||
=================
|
||||
S_SoundList_f
|
||||
=================
|
||||
*/
|
||||
void S_SoundList_f( void )
|
||||
{
|
||||
sfx_t *sfx;
|
||||
int i, samples = 0;
|
||||
|
||||
Msg("\n");
|
||||
Msg(" -samples -hz-- -format- -name--------\n");
|
||||
|
||||
for( i = 0; i < s_numSfx; i++ )
|
||||
{
|
||||
sfx = &s_knownSfx[i];
|
||||
Msg("%4i: ", i);
|
||||
|
||||
if( sfx->loaded )
|
||||
{
|
||||
samples += sfx->samples;
|
||||
Msg("%8i ", sfx->samples);
|
||||
Msg("%5i ", sfx->rate);
|
||||
|
||||
switch( sfx->format )
|
||||
{
|
||||
case AL_FORMAT_STEREO16: Msg("STEREO16 "); break;
|
||||
case AL_FORMAT_STEREO8: Msg("STEREO8 "); break;
|
||||
case AL_FORMAT_MONO16: Msg("MONO16 "); break;
|
||||
case AL_FORMAT_MONO8: Msg("MONO8 "); break;
|
||||
default: Msg("???????? "); break;
|
||||
}
|
||||
|
||||
if( sfx->name[0] == '#' ) Msg("%s", &sfx->name[1]);
|
||||
else Msg("sound/%s", sfx->name);
|
||||
Msg( "\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( sfx->name[0] == '*' ) Msg(" placeholder %s\n", sfx->name);
|
||||
else Msg(" not loaded %s\n", sfx->name);
|
||||
}
|
||||
}
|
||||
|
||||
Msg("-------------------------------------------\n");
|
||||
Msg("%i total samples\n", samples );
|
||||
Msg("%i total sounds\n", s_numSfx );
|
||||
Msg("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
|
||||
WAV LOADING
|
||||
|
||||
=======================================================================
|
||||
*/
|
||||
static byte *iff_data;
|
||||
static byte *iff_dataPtr;
|
||||
static byte *iff_end;
|
||||
static byte *iff_lastChunk;
|
||||
static int iff_chunkLen;
|
||||
|
||||
/*
|
||||
=================
|
||||
S_GetLittleShort
|
||||
=================
|
||||
*/
|
||||
static short S_GetLittleShort( void )
|
||||
{
|
||||
short val = 0;
|
||||
|
||||
val += (*(iff_dataPtr+0) << 0);
|
||||
val += (*(iff_dataPtr+1) << 8);
|
||||
iff_dataPtr += 2;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_GetLittleLong
|
||||
=================
|
||||
*/
|
||||
static int S_GetLittleLong( void )
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
val += (*(iff_dataPtr+0) << 0);
|
||||
val += (*(iff_dataPtr+1) << 8);
|
||||
val += (*(iff_dataPtr+2) << 16);
|
||||
val += (*(iff_dataPtr+3) << 24);
|
||||
iff_dataPtr += 4;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_FindNextChunk
|
||||
=================
|
||||
*/
|
||||
static void S_FindNextChunk( const char *name )
|
||||
{
|
||||
while( 1 )
|
||||
{
|
||||
iff_dataPtr = iff_lastChunk;
|
||||
if( iff_dataPtr >= iff_end )
|
||||
{
|
||||
// didn't find the chunk
|
||||
iff_dataPtr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
iff_dataPtr += 4;
|
||||
iff_chunkLen = S_GetLittleLong();
|
||||
if (iff_chunkLen < 0)
|
||||
{
|
||||
iff_dataPtr = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
iff_dataPtr -= 8;
|
||||
iff_lastChunk = iff_dataPtr + 8 + ((iff_chunkLen + 1) & ~1);
|
||||
if(!com.strncmp( iff_dataPtr, name, 4 ))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_FindChunk
|
||||
=================
|
||||
*/
|
||||
static void S_FindChunk( const char *name )
|
||||
{
|
||||
iff_lastChunk = iff_data;
|
||||
S_FindNextChunk( name );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_LoadWAV
|
||||
=================
|
||||
*/
|
||||
static bool S_LoadWAV( const char *name, byte **wav, wavinfo_t *info )
|
||||
{
|
||||
byte *buffer, *out;
|
||||
int length, samples;
|
||||
|
||||
buffer = FS_LoadFile( name, &length );
|
||||
if( !buffer ) return false;
|
||||
|
||||
iff_data = buffer;
|
||||
iff_end = buffer + length;
|
||||
|
||||
// dind "RIFF" chunk
|
||||
S_FindChunk( "RIFF" );
|
||||
if(!(iff_dataPtr && !com.strncmp(iff_dataPtr+8, "WAVE", 4)))
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: missing 'RIFF/WAVE' chunks (%s)\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get "fmt " chunk
|
||||
iff_data = iff_dataPtr + 12;
|
||||
S_FindChunk("fmt ");
|
||||
if( !iff_dataPtr )
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: missing 'fmt ' chunk (%s)\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
iff_dataPtr += 8;
|
||||
if( S_GetLittleShort() != 1 )
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: microsoft PCM format only (%s)\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
info->channels = S_GetLittleShort();
|
||||
if( info->channels != 1 )
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: only mono WAV files supported (%s)\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
info->rate = S_GetLittleLong();
|
||||
iff_dataPtr += 4+2;
|
||||
|
||||
info->width = S_GetLittleShort() / 8;
|
||||
if( info->width != 1 && info->width != 2 )
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: only 8 and 16 bit WAV files supported (%s)\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get cue chunk
|
||||
S_FindChunk( "cue " );
|
||||
if( iff_dataPtr )
|
||||
{
|
||||
iff_dataPtr += 32;
|
||||
info->loopstart = S_GetLittleLong();
|
||||
S_FindNextChunk( "LIST" ); // if the next chunk is a LIST chunk, look for a cue length marker
|
||||
if( iff_dataPtr )
|
||||
{
|
||||
if( !com.strncmp((const char *)iff_dataPtr + 28, "mark", 4 ))
|
||||
{
|
||||
// this is not a proper parse, but it works with CoolEdit...
|
||||
iff_dataPtr += 24;
|
||||
info->samples = info->loopstart + S_GetLittleLong(); // samples in loop
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info->loopstart = -1;
|
||||
info->samples = 0;
|
||||
}
|
||||
|
||||
// find data chunk
|
||||
S_FindChunk( "data" );
|
||||
if( !iff_dataPtr )
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: missing 'data' chunk (%s)\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
iff_dataPtr += 4;
|
||||
samples = S_GetLittleLong() / info->width;
|
||||
|
||||
if( info->samples )
|
||||
{
|
||||
if( samples < info->samples )
|
||||
{
|
||||
MsgDev( D_ERROR, "S_LoadWAV: %s has a bad loop length\n", name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else info->samples = samples;
|
||||
|
||||
if( info->samples <= 0 )
|
||||
{
|
||||
MsgDev( D_WARN, "S_LoadWAV: file with %i samples (%s)\n", info->samples, name );
|
||||
Mem_Free( buffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the data
|
||||
*wav = out = Z_Malloc( info->samples * info->width );
|
||||
Mem_Copy( out, buffer + (iff_dataPtr - buffer), info->samples * info->width );
|
||||
Mem_Free( buffer );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
|
||||
OGG LOADING
|
||||
|
||||
=======================================================================
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
byte *buffer;
|
||||
ogg_int64_t ind;
|
||||
ogg_int64_t buffsize;
|
||||
} ov_decode_t;
|
||||
|
||||
static size_t ovc_read( void *ptr, size_t size, size_t nb, void *datasource )
|
||||
{
|
||||
ov_decode_t *sound = (ov_decode_t *)datasource;
|
||||
size_t remain, length;
|
||||
|
||||
remain = sound->buffsize - sound->ind;
|
||||
length = size * nb;
|
||||
if( remain < length ) length = remain - remain % size;
|
||||
|
||||
Mem_Copy( ptr, sound->buffer + sound->ind, length );
|
||||
sound->ind += length;
|
||||
|
||||
return length / size;
|
||||
}
|
||||
|
||||
static int ovc_seek ( void *datasource, ogg_int64_t offset, int whence )
|
||||
{
|
||||
ov_decode_t *sound = (ov_decode_t*)datasource;
|
||||
|
||||
switch( whence )
|
||||
{
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
offset += sound->ind;
|
||||
break;
|
||||
case SEEK_END:
|
||||
offset += sound->buffsize;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
if( offset < 0 || offset > sound->buffsize )
|
||||
return -1;
|
||||
|
||||
sound->ind = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ovc_close( void *datasource )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long ovc_tell (void *datasource)
|
||||
{
|
||||
return ((ov_decode_t*)datasource)->ind;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_LoadOGG
|
||||
=================
|
||||
*/
|
||||
static bool S_LoadOGG( const char *name, byte **wav, wavinfo_t *info )
|
||||
{
|
||||
vorbisfile_t vf;
|
||||
vorbis_info_t *vi;
|
||||
vorbis_comment_t *vc;
|
||||
fs_offset_t filesize;
|
||||
ov_decode_t ov_decode;
|
||||
ogg_int64_t length, done = 0;
|
||||
ov_callbacks_t ov_callbacks = { ovc_read, ovc_seek, ovc_close, ovc_tell };
|
||||
byte *data, *buffer;
|
||||
const char *comm;
|
||||
int dummy;
|
||||
long ret;
|
||||
|
||||
// load the file
|
||||
data = FS_LoadFile( name, &filesize );
|
||||
if( !data ) return false;
|
||||
|
||||
// Open it with the VorbisFile API
|
||||
ov_decode.buffer = data;
|
||||
ov_decode.ind = 0;
|
||||
ov_decode.buffsize = filesize;
|
||||
if( ov_open_callbacks( &ov_decode, &vf, NULL, 0, ov_callbacks ) < 0 )
|
||||
{
|
||||
MsgDev( D_ERROR, "S_LoadOGG: couldn't open ogg stream %s\n", name );
|
||||
Mem_Free( data );
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the stream information
|
||||
vi = ov_info( &vf, -1 );
|
||||
if( vi->channels != 1 )
|
||||
{
|
||||
MsgDev( D_ERROR, "S_LoadOGG: only mono OGG files supported (%s)\n", name );
|
||||
ov_clear( &vf );
|
||||
Mem_Free( data );
|
||||
return false;
|
||||
}
|
||||
|
||||
info->channels = vi->channels;
|
||||
info->rate = vi->rate;
|
||||
info->width = 2; // always 16-bit PCM
|
||||
info->loopstart = -1;
|
||||
length = ov_pcm_total( &vf, -1 ) * vi->channels * 2; // 16 bits => "* 2"
|
||||
if( !length )
|
||||
{
|
||||
// bad ogg file
|
||||
MsgDev( D_ERROR, "S_LoadOGG: (%s) is probably corrupted\n", name );
|
||||
ov_clear( &vf );
|
||||
Mem_Free( data );
|
||||
return false;
|
||||
}
|
||||
buffer = (byte *)Z_Malloc( length );
|
||||
|
||||
// decompress ogg into pcm wav format
|
||||
while((ret = ov_read( &vf, &buffer[done], (int)(length - done), big_endian, 2, 1, &dummy )) > 0)
|
||||
done += ret;
|
||||
info->samples = done / ( vi->channels * 2 );
|
||||
vc = ov_comment( &vf, -1 );
|
||||
|
||||
if( vc )
|
||||
{
|
||||
comm = vorbis_comment_query( vc, "LOOP_START", 0 );
|
||||
if( comm )
|
||||
{
|
||||
//FXIME: implement
|
||||
Msg("ogg 'cue' %d\n", com.atoi(comm) );
|
||||
//info->loopstart = bound( 0, com.atoi(comm), info->samples );
|
||||
}
|
||||
}
|
||||
|
||||
// close file
|
||||
ov_clear( &vf );
|
||||
Mem_Free( data );
|
||||
*wav = buffer; // load the data
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_UploadSound
|
||||
=================
|
||||
*/
|
||||
static void S_UploadSound( byte *data, int width, int channels, sfx_t *sfx )
|
||||
{
|
||||
int size;
|
||||
|
||||
// calculate buffer size
|
||||
size = sfx->samples * width * channels;
|
||||
|
||||
// Set buffer format
|
||||
if( width == 2 )
|
||||
{
|
||||
if( channels == 2 ) sfx->format = AL_FORMAT_STEREO16;
|
||||
else sfx->format = AL_FORMAT_MONO16;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( channels == 2 ) sfx->format = AL_FORMAT_STEREO8;
|
||||
else sfx->format = AL_FORMAT_MONO8;
|
||||
}
|
||||
|
||||
// upload the sound
|
||||
palGenBuffers( 1, &sfx->bufferNum );
|
||||
palBufferData( sfx->bufferNum, sfx->format, data, size, sfx->rate );
|
||||
S_CheckForErrors();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_CreateDefaultSound
|
||||
=================
|
||||
*/
|
||||
static void S_CreateDefaultSound( byte **wav, wavinfo_t *info )
|
||||
{
|
||||
byte *out;
|
||||
int i;
|
||||
|
||||
info->rate = 22050;
|
||||
info->width = 2;
|
||||
info->channels = 1;
|
||||
info->samples = 11025;
|
||||
|
||||
*wav = out = Z_Malloc( info->samples * info->width );
|
||||
|
||||
if( s_check_errors->integer )
|
||||
{
|
||||
// create 1 kHz tone as default sound
|
||||
for( i = 0; i < info->samples; i++ )
|
||||
((short *)out)[i] = sin(i * 0.1f) * 20000;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create silent sound
|
||||
for( i = 0; i < info->samples; i++ )
|
||||
((short *)out)[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_LoadSound
|
||||
=================
|
||||
*/
|
||||
loadformat_t load_formats[] =
|
||||
{
|
||||
{"sound/%s.%s", "ogg", S_LoadOGG},
|
||||
{"sound/%s.%s", "wav", S_LoadWAV},
|
||||
{"%s.%s", "ogg", S_LoadOGG},
|
||||
{"%s.%s", "wav", S_LoadWAV},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
bool S_LoadSound( sfx_t *sfx )
|
||||
{
|
||||
byte *data;
|
||||
wavinfo_t info;
|
||||
const char *ext;
|
||||
string loadname, path;
|
||||
loadformat_t *format;
|
||||
bool anyformat;
|
||||
|
||||
if( !sfx ) return false;
|
||||
if( sfx->name[0] == '*' ) return false;
|
||||
if( sfx->loaded ) return true; // see if still in memory
|
||||
|
||||
// load it from disk
|
||||
ext = FS_FileExtension( sfx->name );
|
||||
anyformat = !com.stricmp( ext, "" ) ? true : false;
|
||||
|
||||
com.strncpy( loadname, sfx->name, sizeof( loadname ));
|
||||
FS_StripExtension( loadname ); // remove extension if needed
|
||||
Mem_Set( &info, 0, sizeof( info ));
|
||||
|
||||
// developer warning
|
||||
if( !anyformat ) MsgDev(D_NOTE, "Note: %s will be loading only with ext .%s\n", loadname, ext );
|
||||
|
||||
// now try all the formats in the selected list
|
||||
for( format = load_formats; format->formatstring; format++ )
|
||||
{
|
||||
if( anyformat || !com.stricmp( ext, format->ext ))
|
||||
{
|
||||
com.sprintf( path, format->formatstring, loadname, format->ext );
|
||||
if( format->loadfunc( path, &data, &info ))
|
||||
goto snd_loaded;
|
||||
}
|
||||
}
|
||||
|
||||
sfx->default_sound = true;
|
||||
MsgDev(D_WARN, "FS_LoadSound: couldn't load %s\n", sfx->name );
|
||||
S_CreateDefaultSound( &data, &info );
|
||||
info.loopstart = -1;
|
||||
|
||||
snd_loaded:
|
||||
// load it in
|
||||
sfx->loopstart = info.loopstart;
|
||||
sfx->samples = info.samples;
|
||||
sfx->rate = info.rate;
|
||||
S_UploadSound( data, info.width, info.channels, sfx );
|
||||
sfx->loaded = true;
|
||||
Mem_Free( data );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// Load a sound
|
||||
// =======================================================================
|
||||
/*
|
||||
=================
|
||||
S_FindSound
|
||||
=================
|
||||
*/
|
||||
sfx_t *S_FindSound( const char *name )
|
||||
{
|
||||
sfx_t *sfx;
|
||||
int i;
|
||||
|
||||
if( !name || !name[0] ) return NULL;
|
||||
if( com.strlen(name) >= MAX_STRING )
|
||||
{
|
||||
MsgDev( D_ERROR, "S_FindSound: sound name too long: %s", name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for( i = 0; i < s_numSfx; i++ )
|
||||
{
|
||||
sfx = &s_knownSfx[i];
|
||||
if( !sfx->name[0] ) continue;
|
||||
if( !com.strcmp( name, sfx->name ))
|
||||
{
|
||||
// prolonge registration
|
||||
sfx->registration_sequence = s_registration_sequence;
|
||||
return sfx;
|
||||
}
|
||||
}
|
||||
|
||||
// find a free sfx slot spot
|
||||
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++)
|
||||
{
|
||||
if(!sfx->name[0]) break; // free spot
|
||||
}
|
||||
if( i == s_numSfx )
|
||||
{
|
||||
if( s_numSfx == MAX_SFX )
|
||||
{
|
||||
MsgDev( D_ERROR, "S_FindName: MAX_SFX limit exceeded\n" );
|
||||
return NULL;
|
||||
}
|
||||
s_numSfx++;
|
||||
}
|
||||
|
||||
sfx = &s_knownSfx[i];
|
||||
Mem_Set( sfx, 0, sizeof(*sfx));
|
||||
com.strncpy( sfx->name, name, MAX_STRING );
|
||||
sfx->registration_sequence = s_registration_sequence;
|
||||
|
||||
return sfx;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
S_BeginRegistration
|
||||
=====================
|
||||
*/
|
||||
void S_BeginRegistration( void )
|
||||
{
|
||||
s_registration_sequence++;
|
||||
s_registering = true;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
S_EndRegistration
|
||||
=====================
|
||||
*/
|
||||
void S_EndRegistration( void )
|
||||
{
|
||||
sfx_t *sfx;
|
||||
int i;
|
||||
|
||||
// free any sounds not from this registration sequence
|
||||
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ )
|
||||
{
|
||||
if( !sfx->name[0] ) continue;
|
||||
if( sfx->registration_sequence != s_registration_sequence )
|
||||
{
|
||||
// don't need this sound
|
||||
palDeleteBuffers( 1, &sfx->bufferNum );
|
||||
Mem_Set( sfx, 0, sizeof( sfx_t )); // free spot
|
||||
}
|
||||
}
|
||||
|
||||
// load everything in
|
||||
for( i = 0, sfx = s_knownSfx; i < s_numSfx; i++, sfx++ )
|
||||
{
|
||||
if( !sfx->name[0] )continue;
|
||||
S_LoadSound( sfx );
|
||||
}
|
||||
s_registering = false;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_RegisterSound
|
||||
=================
|
||||
*/
|
||||
sound_t S_RegisterSound( const char *name )
|
||||
{
|
||||
sfx_t *sfx;
|
||||
|
||||
if( !al_state.initialized )
|
||||
return -1;
|
||||
|
||||
sfx = S_FindSound( name );
|
||||
if( !sfx ) return -1;
|
||||
|
||||
sfx->registration_sequence = s_registration_sequence;
|
||||
if( !s_registering ) S_LoadSound( sfx );
|
||||
|
||||
return sfx - s_knownSfx;
|
||||
}
|
||||
|
||||
sfx_t *S_GetSfxByHandle( sound_t handle )
|
||||
{
|
||||
if( handle < 0 || handle >= s_numSfx )
|
||||
{
|
||||
MsgDev( D_ERROR, "S_GetSfxByHandle: handle %i out of range (%i)\n", handle, s_numSfx );
|
||||
return NULL;
|
||||
}
|
||||
return &s_knownSfx[handle];
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
S_FreeSounds
|
||||
=================
|
||||
*/
|
||||
void S_FreeSounds( void )
|
||||
{
|
||||
sfx_t *sfx;
|
||||
int i;
|
||||
|
||||
// stop all sounds
|
||||
S_StopAllSounds();
|
||||
|
||||
// free all sounds
|
||||
for (i = 0; i < s_numSfx; i++)
|
||||
{
|
||||
sfx = &s_knownSfx[i];
|
||||
if( !sfx->loaded ) continue;
|
||||
palDeleteBuffers(1, &sfx->bufferNum);
|
||||
}
|
||||
|
||||
Mem_Set( s_knownSfx, 0, sizeof(s_knownSfx));
|
||||
s_numSfx = 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue